diff --git a/src/component/AppNavigator.js b/src/component/AppNavigator.js index 859f93a..5f35446 100644 --- a/src/component/AppNavigator.js +++ b/src/component/AppNavigator.js @@ -214,7 +214,7 @@ const drawer = createDrawerNavigator( }, }, { - drawerWidth: 280, + drawerWidth: 300, drawerBackgroundColor: 'transparent', headerMode: 'none', backBehavior: 'none', diff --git a/src/component/drawerContent/index.js b/src/component/drawerContent/index.js index 3df7cec..48c9cc6 100644 --- a/src/component/drawerContent/index.js +++ b/src/component/drawerContent/index.js @@ -1,4 +1,18 @@ import { connect } from 'react-redux'; +import { doFetchChannelListMine, selectMyChannelClaims } from 'lbry-redux'; +import { selectUser } from 'lbryinc'; import DrawerContent from './view'; -export default connect()(DrawerContent); +const select = state => ({ + channels: selectMyChannelClaims(state), + user: selectUser(state), +}); + +const perform = dispatch => ({ + fetchChannelListMine: () => dispatch(doFetchChannelListMine()), +}); + +export default connect( + select, + perform +)(DrawerContent); diff --git a/src/component/drawerContent/view.js b/src/component/drawerContent/view.js index 0afd345..4ec8900 100644 --- a/src/component/drawerContent/view.js +++ b/src/component/drawerContent/view.js @@ -1,8 +1,10 @@ import React from 'react'; import { DrawerItems, SafeAreaView } from 'react-navigation'; -import { ScrollView, Text, TouchableOpacity, View } from 'react-native'; +import { Image, ScrollView, Text, TouchableOpacity, View } from 'react-native'; +import Button from 'component/button'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Icon from 'react-native-vector-icons/FontAwesome5'; +import channelIconStyle from 'styles/channelIcon'; import discoverStyle from 'styles/discover'; const groupedMenuItems = { @@ -30,14 +32,73 @@ const groupedMenuItems = { const groupNames = Object.keys(groupedMenuItems); class DrawerContent extends React.PureComponent { + componentDidMount() { + const { fetchChannelListMine } = this.props; + fetchChannelListMine(); + } + + getAvatarImageUrl = () => { + const { channels = [] } = this.props; + if (channels) { + // get the first channel thumbnail found. In the future, allow the user to select a default channel thumbnail? + for (let i = 0; i < channels.length; i++) { + if (channels[i].value && channels[i].value.thumbnail) { + return channels[i].value.thumbnail.url; + } + } + } + + return null; + }; + + launchSignInFlow = () => { + // for now, sync flow (email, then password input) will be the default sign in flow + const { navigation } = this.props; + navigation.navigate({ routeName: 'Verification', key: 'verification', params: { syncFlow: true } }); + }; + render() { - const { activeTintColor, navigation, onItemPress } = this.props; + const { activeTintColor, navigation, user, onItemPress } = this.props; const { state } = navigation; const activeItemKey = state.routes[state.index] ? state.routes[state.index].key : null; + const signedIn = user && user.has_verified_email; + const avatarImageUrl = this.getAvatarImageUrl(); return ( <View style={discoverStyle.drawerContentArea}> + <View style={discoverStyle.signInContainer}> + {!signedIn && ( + <Button + style={discoverStyle.signInButton} + theme={'light'} + text={'Sign in'} + onPress={this.launchSignInFlow} + /> + )} + {signedIn && ( + <View style={discoverStyle.signedIn}> + <View style={discoverStyle.signedInAvatar}> + {avatarImageUrl && ( + <Image + style={discoverStyle.signedInAvatarImage} + resizeMode={'cover'} + source={{ uri: avatarImageUrl }} + /> + )} + {!avatarImageUrl && ( + <Text style={channelIconStyle.autothumbCharacter}> + {user.primary_email.substring(0, 1).toUpperCase()} + </Text> + )} + </View> + <Text style={discoverStyle.signedInEmail} numberOfLines={1}> + {user.primary_email} + </Text> + </View> + )} + </View> + <ScrollView contentContainerStyle={discoverStyle.menuScrollContent}> <SafeAreaView style={discoverStyle.drawerContentContainer} diff --git a/src/component/rewardEnrolment/view.js b/src/component/rewardEnrolment/view.js index f9e74b0..c4b6457 100644 --- a/src/component/rewardEnrolment/view.js +++ b/src/component/rewardEnrolment/view.js @@ -32,7 +32,7 @@ class RewardEnrolment extends React.Component { const { fetching, navigation, unclaimedRewardAmount, user } = this.props; return ( - <View style={rewardStyle.enrollContainer} onPress> + <View style={rewardStyle.enrollContainer}> <View style={rewardStyle.summaryRow}> <Icon name="award" size={36} color={Colors.White} /> <Text style={rewardStyle.summaryText}>{unclaimedRewardAmount} available credits</Text> diff --git a/src/component/walletSignIn/index.js b/src/component/walletSignIn/index.js new file mode 100644 index 0000000..1872254 --- /dev/null +++ b/src/component/walletSignIn/index.js @@ -0,0 +1,19 @@ +import { connect } from 'react-redux'; +import { doToast } from 'lbry-redux'; +import { doSetClientSetting } from 'redux/actions/settings'; +import { selectUser } from 'lbryinc'; +import WalletSignIn from './view'; + +const select = state => ({ + user: selectUser(state), +}); + +const perform = dispatch => ({ + notify: data => dispatch(doToast(data)), + setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)), +}); + +export default connect( + select, + perform +)(WalletSignIn); diff --git a/src/component/walletSignIn/view.js b/src/component/walletSignIn/view.js new file mode 100644 index 0000000..ab88549 --- /dev/null +++ b/src/component/walletSignIn/view.js @@ -0,0 +1,47 @@ +import React from 'react'; +import { Linking, NativeModules, Text, TouchableOpacity, View } from 'react-native'; +import AsyncStorage from '@react-native-community/async-storage'; +import Button from 'component/button'; +import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api +import Link from 'component/link'; +import Colors from 'styles/colors'; +import Icon from 'react-native-vector-icons/FontAwesome5'; +import walletStyle from 'styles/wallet'; + +class WalletSignIn extends React.Component { + onContinuePressed = () => { + const { navigation, setClientSetting } = this.props; + setClientSetting(Constants.SETTING_ALPHA_UNDERSTANDS_RISKS, true); + }; + + onSignInPressed = () => { + const { navigation } = this.props; + navigation.navigate({ routeName: 'Verification', key: 'verification', params: { syncFlow: true } }); + }; + + render() { + const { navigation, user } = this.props; + + return ( + <View style={walletStyle.signInContainer}> + <View style={walletStyle.signInSummaryRow}> + <Text style={walletStyle.signInTitle}>Sign in</Text> + </View> + + <View style={walletStyle.onboarding}> + <Text style={walletStyle.onboardingText}> + An account will allow you to earn rewards and keep your account and settings synced.{'\n\n'} + Without an account, you will not receive rewards, sync and backup services, or security updates.{'\n\n'} + </Text> + </View> + + <View style={walletStyle.buttonRow}> + <Link style={walletStyle.continueLink} text={'Continue anyway'} onPress={this.onContinuePressed} /> + <Button style={walletStyle.signInButton} theme={'light'} text={'Sign in'} onPress={this.onSignInPressed} /> + </View> + </View> + ); + } +} + +export default WalletSignIn; diff --git a/src/constants.js b/src/constants.js index 26659e3..6cbd629 100644 --- a/src/constants.js +++ b/src/constants.js @@ -140,6 +140,8 @@ const Constants = { TRUE_STRING: 'true', MINIMUM_TRANSACTION_BALANCE: 0.1, + + SHARE_BASE_URL: 'https://open.lbry.com', }; export default Constants; diff --git a/src/page/channel/view.js b/src/page/channel/view.js index 2a787be..4432c72 100644 --- a/src/page/channel/view.js +++ b/src/page/channel/view.js @@ -13,7 +13,7 @@ import { } from 'react-native'; import { TabView, SceneMap } from 'react-native-tab-view'; import { normalizeURI } from 'lbry-redux'; -import { navigateBack, getOrderBy } from 'utils/helper'; +import { navigateBack, getOrderBy, formatLbryUrlForWeb } from 'utils/helper'; import ChannelIconItem from 'component/channelIconItem'; import ClaimList from 'component/claimList'; import Colors from 'styles/colors'; @@ -172,6 +172,15 @@ class ChannelPage extends React.PureComponent { } }; + onSharePressed = () => { + const { claim } = this.props; + if (claim) { + const { canonical_url: canonicalUrl, short_url: shortUrl, permanent_url: permanentUrl } = claim; + const url = Constants.SHARE_BASE_URL + formatLbryUrlForWeb(canonicalUrl || shortUrl || permanentUrl); + NativeModules.UtilityModule.shareUrl(url); + } + }; + onDeletePressed = () => { const { abandonClaim, claim, navigation } = this.props; if (claim) { @@ -274,6 +283,12 @@ class ChannelPage extends React.PureComponent { onPress={this.onDeletePressed} /> )} + <Button + style={[channelPageStyle.actionButton, channelPageStyle.shareButton]} + theme={'light'} + icon={'share-alt'} + onPress={this.onSharePressed} + /> {!ownedChannel && <SubscribeButton style={channelPageStyle.subscribeButton} uri={fullUri} name={name} />} {!ownedChannel && ( <SubscribeNotificationButton diff --git a/src/page/file/view.js b/src/page/file/view.js index a79bd0c..0318d8e 100644 --- a/src/page/file/view.js +++ b/src/page/file/view.js @@ -19,7 +19,7 @@ import { WebView, } from 'react-native'; import { NavigationEvents } from 'react-navigation'; -import { navigateBack, navigateToUri } from 'utils/helper'; +import { navigateBack, navigateToUri, formatLbryUrlForWeb } from 'utils/helper'; import Icon from 'react-native-vector-icons/FontAwesome5'; import ImageViewer from 'react-native-image-zoom-viewer'; import Button from 'component/button'; @@ -508,16 +508,11 @@ class FilePage extends React.PureComponent { this.setState({ fileViewLogged: true }); }; - // TODO: Move this to lbry-redux - formatLbryUrlForWeb = url => { - return url.replace('lbry://', '/').replace(/#/g, ':'); - }; - handleSharePress = () => { const { claim, notify } = this.props; if (claim) { const { canonical_url: canonicalUrl, short_url: shortUrl, permanent_url: permanentUrl } = claim; - const url = 'https://open.lbry.com' + this.formatLbryUrlForWeb(canonicalUrl || shortUrl || permanentUrl); + const url = Constants.SHARE_BASE_URL + formatLbryUrlForWeb(canonicalUrl || shortUrl || permanentUrl); NativeModules.UtilityModule.shareUrl(url); } }; diff --git a/src/page/wallet/view.js b/src/page/wallet/view.js index 4c444b7..6ce9ba7 100644 --- a/src/page/wallet/view.js +++ b/src/page/wallet/view.js @@ -5,6 +5,7 @@ import WalletAddress from 'component/walletAddress'; import WalletBalance from 'component/walletBalance'; import WalletSend from 'component/walletSend'; import WalletRewardsDriver from 'component/walletRewardsDriver'; +import WalletSignIn from 'component/walletSignIn'; import WalletSyncDriver from 'component/walletSyncDriver'; import Button from 'component/button'; import Link from 'component/link'; @@ -66,33 +67,15 @@ class WalletPage extends React.PureComponent { understandsRisks, setClientSetting, navigation, + user, } = this.props; - if (!understandsRisks) { + const signedIn = user && user.has_verified_email; + if (!signedIn && !understandsRisks) { return ( - <View> + <View style={walletStyle.container}> <UriBar navigation={navigation} /> - <View style={walletStyle.warning}> - <Text style={walletStyle.warningParagraph}> - This is beta software. You may lose any credits that you send to your wallet due to software bugs, deleted - files, or malicious third-party software. You should not use this wallet as your primary wallet. - </Text> - {!hasSyncedWallet && ( - <Text style={walletStyle.warningParagraph}> - Since you are not using the LBRY sync service, you will lose all of your credits if you uninstall this - application. Instructions on how to enroll as well as how to backup your wallet manually are available - on the next page. - </Text> - )} - <Text style={walletStyle.warningText}> - If you understand the risks and you wish to continue, please tap the button below. - </Text> - </View> - <Button - text={'I understand the risks'} - style={[walletStyle.button, walletStyle.understand]} - onPress={() => setClientSetting(Constants.SETTING_ALPHA_UNDERSTANDS_RISKS, true)} - /> + <WalletSignIn navigation={navigation} /> </View> ); } diff --git a/src/styles/channelPage.js b/src/styles/channelPage.js index d9963da..ba27579 100644 --- a/src/styles/channelPage.js +++ b/src/styles/channelPage.js @@ -172,6 +172,10 @@ const channelPageStyle = StyleSheet.create({ deleteButton: { marginLeft: 8, }, + shareButton: { + marginLeft: 8, + marginRight: 8, + }, }); export default channelPageStyle; diff --git a/src/styles/discover.js b/src/styles/discover.js index 5aeccba..1904531 100644 --- a/src/styles/discover.js +++ b/src/styles/discover.js @@ -296,7 +296,7 @@ const discoverStyle = StyleSheet.create({ marginRight: 6, }, menuScrollContent: { - paddingTop: 16, + paddingTop: 12, }, menuGroup: { marginTop: 8, @@ -336,6 +336,37 @@ const discoverStyle = StyleSheet.create({ fontFamily: 'Inter-UI-Regular', fontSize: 16, }, + signInContainer: { + backgroundColor: Colors.LbryGreen, + height: 140, + padding: 16, + }, + signInButton: { + backgroundColor: Colors.White, + alignSelf: 'flex-start', + position: 'absolute', + bottom: 16, + left: 16, + }, + signedInEmail: { + fontFamily: 'Inter-UI-SemiBold', + fontSize: 15, + color: Colors.White, + }, + signedInAvatar: { + backgroundColor: Colors.NextLbryGreen, + width: 80, + height: 80, + marginBottom: 12, + borderRadius: 160, + overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center', + }, + signedInAvatarImage: { + width: '100%', + height: '100%', + }, }); export default discoverStyle; diff --git a/src/styles/wallet.js b/src/styles/wallet.js index fbd7174..da827a3 100644 --- a/src/styles/wallet.js +++ b/src/styles/wallet.js @@ -305,6 +305,52 @@ const walletStyle = StyleSheet.create({ fontSize: 16, marginLeft: 8, }, + buttonRow: { + width: '100%', + position: 'absolute', + alignItems: 'center', + left: 24, + bottom: 24, + flexDirection: 'row', + justifyContent: 'space-between', + }, + continueLink: { + fontSize: 14, + fontFamily: 'Inter-UI-Regular', + color: Colors.White, + }, + learnMoreLink: { + fontFamily: 'Inter-UI-Regular', + color: Colors.NextLbryGreen, + }, + signInButton: { + backgroundColor: Colors.White, + paddingLeft: 16, + paddingRight: 16, + }, + signInContainer: { + flex: 1, + marginTop: 60, + padding: 24, + backgroundColor: Colors.LbryGreen, + }, + onboarding: { + marginTop: 36, + }, + onboardingText: { + fontFamily: 'Inter-UI-Regular', + fontSize: 18, + lineHeight: 28, + color: Colors.White, + }, + signInSummaryRow: { + flexDirection: 'row', + }, + signInTitle: { + color: Colors.White, + fontFamily: 'Inter-UI-Regular', + fontSize: 28, + }, }); export default walletStyle; diff --git a/src/utils/helper.js b/src/utils/helper.js index 0126067..200dfaf 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -346,3 +346,8 @@ export function uploadImageAsset(filePath, success, failure) { } }); } + +// TODO: Move this to lbry-redux +export function formatLbryUrlForWeb(url) { + return url.replace('lbry://', '/').replace(/#/g, ':'); +}