Mobile subscriptions (#382)

* add subscribe button to file page
* add My Subscriptions page
* get module resolve babel plugin working to eliminate ugly imports
This commit is contained in:
Akinwale Ariwodola 2019-01-07 08:26:47 +01:00 committed by GitHub
parent 0cc2b4e368
commit 6991b99ea9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 414 additions and 28 deletions

8
app/.babelrc Normal file
View file

@ -0,0 +1,8 @@
{
"presets": ["react-native"],
"plugins": [
["module-resolver", {
root: ["./src"],
}],
]
}

111
app/package-lock.json generated
View file

@ -644,6 +644,56 @@
} }
} }
}, },
"@expo/vector-icons": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-8.1.0.tgz",
"integrity": "sha512-/aKa+bgp3LIcTKJWPLRYTYCL0wf/Fr4dwl4XYmNGFG092pK3McuBoDk3b8tWyZnXBnEiqnMLd6Qwr+LEX6Jc0Q==",
"requires": {
"lodash": "^4.17.4",
"react-native-vector-icons": "6.0.0"
},
"dependencies": {
"prop-types": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
"integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
"requires": {
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"react-native-vector-icons": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-6.0.0.tgz",
"integrity": "sha512-uF3oWb3TV42uXi2apVOZHw9oy9Nr5SXDVwOo1umQWo/yYCrDzXyVfq14DzezgEbJ9jfc/yghBelj0agkXmOKlg==",
"requires": {
"lodash": "^4.0.0",
"prop-types": "^15.6.2",
"yargs": "^8.0.2"
}
},
"yargs": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz",
"integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=",
"requires": {
"camelcase": "^4.1.0",
"cliui": "^3.2.0",
"decamelize": "^1.1.1",
"get-caller-file": "^1.0.1",
"os-locale": "^2.0.0",
"read-pkg-up": "^2.0.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^7.0.0"
}
}
}
},
"absolute-path": { "absolute-path": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz", "resolved": "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz",
@ -1312,6 +1362,19 @@
"babel-runtime": "^6.22.0" "babel-runtime": "^6.22.0"
} }
}, },
"babel-plugin-module-resolver": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.1.1.tgz",
"integrity": "sha512-1Q77Al4ydp6nYApJ7sQ2fmgz30WuQgJZegIYuyOdbdpxenB/bSezQ3hDPsumIXGlUS4vUIv+EwFjzzXZNWtARw==",
"dev": true,
"requires": {
"find-babel-config": "^1.1.0",
"glob": "^7.1.2",
"pkg-up": "^2.0.0",
"reselect": "^3.0.1",
"resolve": "^1.4.0"
}
},
"babel-plugin-react-transform": { "babel-plugin-react-transform": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-react-transform/-/babel-plugin-react-transform-3.0.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-react-transform/-/babel-plugin-react-transform-3.0.0.tgz",
@ -2099,6 +2162,11 @@
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.28.tgz", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.28.tgz",
"integrity": "sha512-OJT3rzgtsYca/5WmmEuFJDPMwROVh5SSjoEX9wIrpfbbWJ4KqRzShs8Cj6jWHaatBYAeWngBA+kmmrcHSklT1g==" "integrity": "sha512-OJT3rzgtsYca/5WmmEuFJDPMwROVh5SSjoEX9wIrpfbbWJ4KqRzShs8Cj6jWHaatBYAeWngBA+kmmrcHSklT1g=="
}, },
"bluebird": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
"integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
},
"bplist-creator": { "bplist-creator": {
"version": "0.0.7", "version": "0.0.7",
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz",
@ -2822,6 +2890,16 @@
"unpipe": "~1.0.0" "unpipe": "~1.0.0"
} }
}, },
"find-babel-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.1.0.tgz",
"integrity": "sha1-rMAQQ6Z0n+w0Qpvmtk9ULrtdY1U=",
"dev": true,
"requires": {
"json5": "^0.5.1",
"path-exists": "^3.0.0"
}
},
"find-cache-dir": { "find-cache-dir": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
@ -3998,20 +4076,27 @@
} }
}, },
"lbryinc": { "lbryinc": {
"version": "github:lbryio/lbryinc#82308ece97188747adbf6d71d55b6e7a6fa0bd95", "version": "github:lbryio/lbryinc#4d24cef00106294d3e848b6c2b261e01382fce17",
"from": "github:lbryio/lbryinc", "from": "github:lbryio/lbryinc#subscriptions",
"requires": { "requires": {
"lbry-redux": "github:lbryio/lbry-redux#2375860d6269d0369418879c2531b1d48c4e47f2", "bluebird": "^3.5.1",
"lbry-redux": "github:lbryio/lbry-redux#84b7d396934d57a37802aadbef71db91230a9404",
"reselect": "^3.0.0" "reselect": "^3.0.0"
}, },
"dependencies": { "dependencies": {
"lbry-redux": { "lbry-redux": {
"version": "github:lbryio/lbry-redux#2375860d6269d0369418879c2531b1d48c4e47f2", "version": "github:lbryio/lbry-redux#84b7d396934d57a37802aadbef71db91230a9404",
"from": "github:lbryio/lbry-redux#2375860d6269d0369418879c2531b1d48c4e47f2", "from": "github:lbryio/lbry-redux#84b7d396934d57a37802aadbef71db91230a9404",
"requires": { "requires": {
"proxy-polyfill": "0.1.6", "proxy-polyfill": "0.1.6",
"reselect": "^3.0.0" "reselect": "^3.0.0",
"uuid": "^3.3.2"
} }
},
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
} }
} }
}, },
@ -4978,6 +5063,15 @@
"find-up": "^2.1.0" "find-up": "^2.1.0"
} }
}, },
"pkg-up": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
"integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
"dev": true,
"requires": {
"find-up": "^2.1.0"
}
},
"plist": { "plist": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz",
@ -7026,6 +7120,11 @@
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
}, },
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
},
"yallist": { "yallist": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",

View file

@ -7,8 +7,9 @@
}, },
"dependencies": { "dependencies": {
"base-64": "^0.1.0", "base-64": "^0.1.0",
"@expo/vector-icons": "^8.1.0",
"lbry-redux": "lbryio/lbry-redux", "lbry-redux": "lbryio/lbry-redux",
"lbryinc": "lbryio/lbryinc", "lbryinc": "lbryio/lbryinc#subscriptions",
"moment": "^2.22.1", "moment": "^2.22.1",
"react": "16.2.0", "react": "16.2.0",
"react-native": "0.55.3", "react-native": "0.55.3",
@ -34,6 +35,7 @@
"devDependencies": { "devDependencies": {
"babel-preset-env": "^1.6.1", "babel-preset-env": "^1.6.1",
"babel-preset-stage-2": "^6.18.0", "babel-preset-stage-2": "^6.18.0",
"babel-plugin-module-resolver": "^3.1.1",
"flow-babel-webpack-plugin": "^1.1.1" "flow-babel-webpack-plugin": "^1.1.1"
} }
} }

View file

@ -9,6 +9,7 @@ import TrendingPage from '../page/trending';
import SearchPage from '../page/search'; import SearchPage from '../page/search';
import SettingsPage from '../page/settings'; import SettingsPage from '../page/settings';
import SplashScreen from '../page/splash'; import SplashScreen from '../page/splash';
import SubscriptionsPage from '../page/subscriptions';
import TransactionHistoryPage from '../page/transactionHistory'; import TransactionHistoryPage from '../page/transactionHistory';
import WalletPage from '../page/wallet'; import WalletPage from '../page/wallet';
import SearchInput from '../component/searchInput'; import SearchInput from '../component/searchInput';
@ -109,6 +110,17 @@ const myLbryStack = createStackNavigator({
} }
}); });
const mySubscriptionsStack = createStackNavigator({
Subscriptions: {
screen: SubscriptionsPage,
navigationOptions: ({ navigation }) => ({
title: 'My Subscriptions',
headerLeft: menuNavigationButton(navigation),
headerTitleStyle: discoverStyle.titleText
})
}
});
const rewardsStack = createStackNavigator({ const rewardsStack = createStackNavigator({
Rewards: { Rewards: {
screen: RewardsPage, screen: RewardsPage,
@ -148,6 +160,9 @@ const drawer = createDrawerNavigator({
TrendingStack: { screen: trendingStack, navigationOptions: { TrendingStack: { screen: trendingStack, navigationOptions: {
title: 'Trending', drawerIcon: ({ tintColor }) => <Icon name="fire" size={20} style={{ color: tintColor }} /> title: 'Trending', drawerIcon: ({ tintColor }) => <Icon name="fire" size={20} style={{ color: tintColor }} />
}}, }},
MySubscriptionsStack: { screen: mySubscriptionsStack, navigationOptions: {
title: 'My Subscriptions', drawerIcon: ({ tintColor }) => <Icon name="rss-square" size={20} style={{ color: tintColor }} />
}},
MyLBRYStack: { screen: myLbryStack, navigationOptions: { MyLBRYStack: { screen: myLbryStack, navigationOptions: {
title: 'My LBRY', drawerIcon: ({ tintColor }) => <Icon name="folder" size={20} style={{ color: tintColor }} /> title: 'My LBRY', drawerIcon: ({ tintColor }) => <Icon name="folder" size={20} style={{ color: tintColor }} />
}}, }},

View file

@ -11,6 +11,8 @@ export default class Button extends React.PureComponent {
style, style,
text, text,
icon, icon,
iconColor,
solid,
theme, theme,
onPress, onPress,
onLayout onLayout
@ -41,9 +43,14 @@ export default class Button extends React.PureComponent {
textStyles.push(buttonStyle.textLight); textStyles.push(buttonStyle.textLight);
} }
let renderIcon = (<Icon name={icon} size={18} color={iconColor ? iconColor : ('light' === theme ? Colors.DarkGrey : Colors.White)} />);
if (solid) {
renderIcon = (<Icon name={icon} size={18} color={iconColor ? iconColor : ('light' === theme ? Colors.DarkGrey : Colors.White)} solid />);
}
return ( return (
<TouchableOpacity disabled={disabled} style={styles} onPress={onPress} onLayout={onLayout}> <TouchableOpacity disabled={disabled} style={styles} onPress={onPress} onLayout={onLayout}>
{icon && <Icon name={icon} size={18} color={'light' === theme ? Colors.DarkGrey : Colors.White} />} {icon && renderIcon}
{text && (text.trim().length > 0) && <Text style={textStyles}>{text}</Text>} {text && (text.trim().length > 0) && <Text style={textStyles}>{text}</Text>}
</TouchableOpacity> </TouchableOpacity>
); );

View file

@ -0,0 +1,23 @@
import { connect } from 'react-redux';
import {
doChannelSubscribe,
doChannelUnsubscribe,
selectSubscriptions,
makeSelectIsSubscribed,
} from 'lbryinc';
import { doToast } from 'lbry-redux';
import SubscribeButton from './view';
const select = (state, props) => ({
subscriptions: selectSubscriptions(state),
isSubscribed: makeSelectIsSubscribed(props.uri, true)(state),
});
export default connect(
select,
{
doChannelSubscribe,
doChannelUnsubscribe,
doToast,
}
)(SubscribeButton);

View file

@ -0,0 +1,49 @@
import React from 'react';
import { parseURI } from 'lbry-redux';
import { NativeModules, Text, View, TouchableOpacity } from 'react-native';
import Button from '../button';
import Colors from '../../styles/colors';
class SubscribeButton extends React.PureComponent {
render() {
const {
uri,
isSubscribed,
doChannelSubscribe,
doChannelUnsubscribe,
style
} = this.props;
let styles = [];
if (style) {
if (style.length) {
styles = styles.concat(style);
} else {
styles.push(style);
}
}
const iconColor = isSubscribed ? null : Colors.Red;
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
const subscriptionLabel = isSubscribed ? __('Unsubscribe') : __('Subscribe');
const { claimName } = parseURI(uri);
return (
<Button
style={styles}
theme={"light"}
icon={"heart"}
iconColor={iconColor}
solid={isSubscribed ? false : true}
text={subscriptionLabel}
onPress={() => {
subscriptionHandler({
channelName: claimName,
uri,
});
}} />
);
}
}
export default SubscribeButton;

View file

@ -19,7 +19,7 @@ import {
searchReducer, searchReducer,
walletReducer walletReducer
} from 'lbry-redux'; } from 'lbry-redux';
import { authReducer, rewardsReducer, userReducer } from 'lbryinc'; import { authReducer, rewardsReducer, subscriptionsReducer, userReducer } from 'lbryinc';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'; import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import { createLogger } from 'redux-logger'; import { createLogger } from 'redux-logger';
import { AppNavigator } from './component/AppNavigator'; import { AppNavigator } from './component/AppNavigator';
@ -87,6 +87,7 @@ const reducers = combineReducers({
rewards: rewardsReducer, rewards: rewardsReducer,
settings: settingsReducer, settings: settingsReducer,
search: searchReducer, search: searchReducer,
subscriptions: subscriptionsReducer,
user: userReducer, user: userReducer,
wallet: walletReducer wallet: walletReducer
}); });

View file

@ -9,7 +9,7 @@ import {
View, View,
ScrollView ScrollView
} from 'react-native'; } from 'react-native';
import { navigateToUri } from '../../utils/helper'; import { navigateToUri, uriFromFileInfo } from '../../utils/helper';
import Colors from '../../styles/colors'; import Colors from '../../styles/colors';
import PageHeader from '../../component/pageHeader'; import PageHeader from '../../component/pageHeader';
import FileListItem from '../../component/fileListItem'; import FileListItem from '../../component/fileListItem';
@ -28,14 +28,6 @@ class DownloadsPage extends React.PureComponent {
this.props.fileList(); this.props.fileList();
} }
uriFromFileInfo(fileInfo) {
const { name: claimName, claim_name: claimNameDownloaded, claim_id: claimId } = fileInfo;
const uriParams = {};
uriParams.contentName = claimName || claimNameDownloaded;
uriParams.claimId = claimId;
return buildURI(uriParams);
}
render() { render() {
const { fetching, fileInfos, navigation } = this.props; const { fetching, fileInfos, navigation } = this.props;
const hasDownloads = fileInfos && Object.values(fileInfos).length > 0; const hasDownloads = fileInfos && Object.values(fileInfos).length > 0;
@ -59,9 +51,9 @@ class DownloadsPage extends React.PureComponent {
renderItem={ ({item}) => ( renderItem={ ({item}) => (
<FileListItem <FileListItem
style={fileListStyle.item} style={fileListStyle.item}
uri={this.uriFromFileInfo(item)} uri={uriFromFileInfo(item)}
navigation={navigation} navigation={navigation}
onPress={() => navigateToUri(navigation, this.uriFromFileInfo(item), { autoplay: true })} /> onPress={() => navigateToUri(navigation, uriFromFileInfo(item), { autoplay: true })} />
) )
} }
data={fileInfos.sort((a, b) => { data={fileInfos.sort((a, b) => {

View file

@ -8,6 +8,7 @@ import {
makeSelectIsUriResolving, makeSelectIsUriResolving,
makeSelectCostInfoForUri, makeSelectCostInfoForUri,
makeSelectFileInfoForUri, makeSelectFileInfoForUri,
makeSelectChannelForClaimUri,
makeSelectClaimForUri, makeSelectClaimForUri,
makeSelectContentTypeForUri, makeSelectContentTypeForUri,
makeSelectMetadataForUri, makeSelectMetadataForUri,
@ -32,6 +33,7 @@ const select = (state, props) => {
//tab: makeSelectCurrentParam('tab')(state), //tab: makeSelectCurrentParam('tab')(state),
fileInfo: makeSelectFileInfoForUri(selectProps.uri)(state), fileInfo: makeSelectFileInfoForUri(selectProps.uri)(state),
rewardedContentClaimIds: selectRewardContentClaimIds(state, selectProps), rewardedContentClaimIds: selectRewardContentClaimIds(state, selectProps),
channelUri: makeSelectChannelForClaimUri(selectProps.uri, true)(state),
}; };
}; };

View file

@ -27,6 +27,7 @@ import FloatingWalletBalance from '../../component/floatingWalletBalance';
import Link from '../../component/link'; import Link from '../../component/link';
import MediaPlayer from '../../component/mediaPlayer'; import MediaPlayer from '../../component/mediaPlayer';
import RelatedContent from '../../component/relatedContent'; import RelatedContent from '../../component/relatedContent';
import SubscribeButton from '../../component/subscribeButton';
import UriBar from '../../component/uriBar'; import UriBar from '../../component/uriBar';
import Video from 'react-native-video'; import Video from 'react-native-video';
import filePageStyle from '../../styles/filePage'; import filePageStyle from '../../styles/filePage';
@ -332,6 +333,7 @@ class FilePage extends React.PureComponent {
render() { render() {
const { const {
claim, claim,
channelUri,
fileInfo, fileInfo,
metadata, metadata,
contentType, contentType,
@ -403,7 +405,6 @@ class FilePage extends React.PureComponent {
value && value.publisherSignature && value.publisherSignature.certificateId; value && value.publisherSignature && value.publisherSignature.certificateId;
const canSendTip = this.state.tipAmount > 0; const canSendTip = this.state.tipAmount > 0;
const playerStyle = [filePageStyle.player, const playerStyle = [filePageStyle.player,
this.state.isLandscape ? filePageStyle.containedPlayerLandscape : this.state.isLandscape ? filePageStyle.containedPlayerLandscape :
(this.state.fullscreenMode ? filePageStyle.fullscreenPlayer : filePageStyle.containedPlayer)]; (this.state.fullscreenMode ? filePageStyle.fullscreenPlayer : filePageStyle.containedPlayer)];
@ -494,11 +495,16 @@ class FilePage extends React.PureComponent {
{showActions && {showActions &&
<View style={filePageStyle.actions}> <View style={filePageStyle.actions}>
<View style={filePageStyle.socialActions}>
{channelName && <SubscribeButton
style={[filePageStyle.actionButton, filePageStyle.subscribeButton]}
uri={channelUri} name={channelName} />}
{<Button style={filePageStyle.actionButton} {<Button style={filePageStyle.actionButton}
theme={"light"} theme={"light"}
icon={"gift"} icon={"gift"}
text={"Send a tip"} text={"Send a tip"}
onPress={() => this.setState({ showTipView: true })} />} onPress={() => this.setState({ showTipView: true })} />}
</View>
{showFileActions && {showFileActions &&
<View style={filePageStyle.fileActions}> <View style={filePageStyle.fileActions}>
{completed && <Button style={filePageStyle.actionButton} {completed && <Button style={filePageStyle.actionButton}

View file

@ -2,6 +2,7 @@ import { connect } from 'react-redux';
import { doBalanceSubscribe, doBlackListedOutpointsSubscribe, doToast } from 'lbry-redux'; import { doBalanceSubscribe, doBlackListedOutpointsSubscribe, doToast } from 'lbry-redux';
import { import {
doAuthenticate, doAuthenticate,
doCheckSubscriptionsInit,
doFetchRewardedContent, doFetchRewardedContent,
doUserEmailToVerify, doUserEmailToVerify,
doUserEmailVerify, doUserEmailVerify,
@ -21,6 +22,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()),
checkSubscriptionsInit: () => dispatch(doCheckSubscriptionsInit()),
deleteCompleteBlobs: () => dispatch(doDeleteCompleteBlobs()), deleteCompleteBlobs: () => dispatch(doDeleteCompleteBlobs()),
fetchRewardedContent: () => dispatch(doFetchRewardedContent()), fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
notify: data => dispatch(doToast(data)), notify: data => dispatch(doToast(data)),

View file

@ -144,12 +144,14 @@ class SplashScreen extends React.PureComponent {
authenticate, authenticate,
balanceSubscribe, balanceSubscribe,
blacklistedOutpointsSubscribe, blacklistedOutpointsSubscribe,
checkSubscriptionsInit,
navigation, navigation,
notify notify
} = this.props; } = this.props;
balanceSubscribe(); balanceSubscribe();
blacklistedOutpointsSubscribe(); blacklistedOutpointsSubscribe();
checkSubscriptionsInit();
NativeModules.VersionInfo.getAppVersion().then(appVersion => { NativeModules.VersionInfo.getAppVersion().then(appVersion => {
this.setState({ shouldAuthenticate: true }); this.setState({ shouldAuthenticate: true });
authenticate(appVersion, Platform.OS); authenticate(appVersion, Platform.OS);

View file

@ -0,0 +1,40 @@
import { connect } from 'react-redux';
import {
doFetchMySubscriptions,
doSetViewMode,
doFetchRecommendedSubscriptions,
doCompleteFirstRun,
doShowSuggestedSubs,
selectSubscriptionClaims,
selectSubscriptions,
selectSubscriptionsBeingFetched,
selectIsFetchingSubscriptions,
selectUnreadSubscriptions,
selectViewMode,
selectFirstRunCompleted,
selectShowSuggestedSubs
} from 'lbryinc';
import SubscriptionsPage from './view';
const select = state => ({
loading:
selectIsFetchingSubscriptions(state) ||
Boolean(Object.keys(selectSubscriptionsBeingFetched(state)).length),
subscribedChannels: selectSubscriptions(state),
allSubscriptions: selectSubscriptionClaims(state),
unreadSubscriptions: selectUnreadSubscriptions(state),
viewMode: selectViewMode(state),
firstRunCompleted: selectFirstRunCompleted(state),
showSuggestedSubs: selectShowSuggestedSubs(state),
});
export default connect(
select,
{
doFetchMySubscriptions,
doSetViewMode,
doFetchRecommendedSubscriptions,
doCompleteFirstRun,
doShowSuggestedSubs,
}
)(SubscriptionsPage);

View file

@ -0,0 +1,89 @@
import React from 'react';
import NavigationActions from 'react-navigation';
import {
ActivityIndicator,
AsyncStorage,
FlatList,
NativeModules,
SectionList,
Text,
View
} from 'react-native';
import { buildURI } from 'lbry-redux';
import { uriFromFileInfo } from 'utils/helper';
import moment from 'moment';
import Colors from 'styles/colors';
import discoverStyle from 'styles/discover';
import subscriptionsStyle from 'styles/subscriptions';
import FloatingWalletBalance from 'component/floatingWalletBalance';
import FileItem from 'component/fileItem';
import UriBar from 'component/uriBar';
class SubscriptionsPage extends React.PureComponent {
componentDidMount() {
const {
doFetchMySubscriptions,
doFetchRecommendedSubscriptions
} = this.props;
doFetchMySubscriptions();
doFetchRecommendedSubscriptions();
}
render() {
const {
subscribedChannels,
allSubscriptions,
loading,
viewMode,
doSetViewMode,
loadingSuggested,
firstRunCompleted,
doCompleteFirstRun,
doShowSuggestedSubs,
showSuggestedSubs,
unreadSubscriptions,
navigation
} = this.props;
const numberOfSubscriptions = subscribedChannels ? subscribedChannels.length : 0;
const hasSubscriptions = numberOfSubscriptions > 0;
return (
<View style={subscriptionsStyle.container}>
{hasSubscriptions && !loading &&
<FlatList
style={subscriptionsStyle.scrollContainer}
contentContainerStyle={subscriptionsStyle.scrollPadding}
renderItem={ ({item}) => (
<FileItem
style={subscriptionsStyle.fileItem}
mediaStyle={discoverStyle.fileItemMedia}
key={item}
uri={uriFromFileInfo(item)}
navigation={navigation} />
)
}
data={allSubscriptions}
keyExtractor={(item, index) => uriFromFileInfo(item)} />}
{hasSubscriptions && loading &&
<View style={subscriptionsStyle.busyContainer}>
<ActivityIndicator size="large" color={Colors.LbryGreen} style={subscriptionsStyle.loading} />
</View>
}
{!hasSubscriptions &&
<View style={subscriptionsStyle.busyContainer}>
<Text style={subscriptionsStyle.infoText}>
You are not subscribed to any channels. Feel free to discover new channels that you can subscribe to.
</Text>
</View>}
<FloatingWalletBalance navigation={navigation} />
<UriBar navigation={navigation} />
</View>
)
}
}
export default SubscriptionsPage;

View file

@ -154,11 +154,18 @@ const filePageStyle = StyleSheet.create({
fileActions: { fileActions: {
alignSelf: 'flex-end' alignSelf: 'flex-end'
}, },
socialActions: {
alignSelf: 'flex-start',
flexDirection: 'row'
},
actionButton: { actionButton: {
alignSelf: 'flex-start', alignSelf: 'flex-start',
backgroundColor: Colors.White, backgroundColor: Colors.White,
paddingLeft: 24, paddingLeft: 16,
paddingRight: 24 paddingRight: 16
},
subscribeButton: {
marginRight: 8
}, },
loading: { loading: {
position: 'absolute', position: 'absolute',

View file

@ -0,0 +1,33 @@
import { StyleSheet } from 'react-native';
const subscriptionsStyle = StyleSheet.create({
container: {
flex: 1,
},
busyContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
padding: 16
},
scrollContainer: {
flex: 1,
marginBottom: 60
},
scrollPadding: {
paddingTop: 24
},
infoText: {
textAlign: 'center',
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
},
fileItem: {
marginLeft: 24,
marginRight: 24,
marginBottom: 24
}
});
export default subscriptionsStyle;

View file

@ -1,4 +1,5 @@
import { NavigationActions, StackActions } from 'react-navigation'; import { NavigationActions, StackActions } from 'react-navigation';
import { buildURI } from 'lbry-redux';
import Constants from '../constants'; import Constants from '../constants';
function getRouteForSpecialUri(uri) { function getRouteForSpecialUri(uri) {
@ -83,3 +84,11 @@ export function navigateToUri(navigation, uri, additionalParams) {
navigation.navigate({ routeName: 'File', key: uri, params }); navigation.navigate({ routeName: 'File', key: uri, params });
} }
export function uriFromFileInfo(fileInfo) {
const { name: claimName, claim_name: claimNameDownloaded, claim_id: claimId } = fileInfo;
const uriParams = {};
uriParams.contentName = claimName || claimNameDownloaded;
uriParams.claimId = claimId;
return buildURI(uriParams);
}