tip button on channels, modal send tip component

This commit is contained in:
Akinwale Ariwodola 2019-12-16 18:54:06 +01:00
parent 8b4f1de951
commit 3a20cccd2a
6 changed files with 223 additions and 2 deletions

View file

@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import { doSendTip, doToast, selectBalance } from 'lbry-redux';
import ModalTipView from './view';
const select = state => ({
balance: selectBalance(state),
});
const perform = dispatch => ({
notify: data => dispatch(doToast(data)),
sendTip: (amount, claimId, isSupport, successCallback, errorCallback) =>
dispatch(doSendTip(amount, claimId, isSupport, successCallback, errorCallback)),
});
export default connect(
select,
perform
)(ModalTipView);

View file

@ -0,0 +1,123 @@
import React from 'react';
import { ActivityIndicator, Alert, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { formatCredits } from 'lbry-redux';
import modalStyle from 'styles/modal';
import modalTipStyle from 'styles/modalTip';
import Button from 'component/button';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link';
export default class ModalTipView extends React.PureComponent {
state = {
creditsInputFocused: false,
sendTipStarted: false,
tipAmount: null,
};
handleSendTip = () => {
const { claim, balance, notify, onSendTipFailed, onSendTipSuccessful, sendTip } = this.props;
const { tipAmount } = this.state;
if (tipAmount > balance) {
notify({
message: 'Insufficient credits',
});
return;
}
const amount = parseInt(tipAmount, 10);
const message =
amount === 1
? __('Are you sure you want to tip %amount% credit', { amount })
: __('Are you sure you want to tip %amount% credits', { amount });
Alert.alert(
__('Send tip'),
message,
[
{ text: __('No') },
{
text: __('Yes'),
onPress: () => {
this.setState({ sendTipStarted: true }, () =>
sendTip(
tipAmount,
claim.claim_id,
false,
() => {
// success
this.setState({ tipAmount: null, sendTipStarted: false });
if (onSendTipSuccessful) onSendTipSuccessful();
},
() => {
// error
if (onSendTipFailed) onSendTipFailed();
}
)
);
},
},
],
{ cancelable: true }
);
};
render() {
const { balance, onCancelPress, onOverlayPress } = this.props;
const canSendTip = this.state.tipAmount > 0;
return (
<TouchableOpacity style={modalStyle.overlay} activeOpacity={1} onPress={onOverlayPress}>
<TouchableOpacity style={modalStyle.container} activeOpacity={1}>
<View style={modalTipStyle.container}>
<Text style={modalTipStyle.title}>{__('Send a tip')}</Text>
<View style={modalTipStyle.row}>
<View style={modalTipStyle.amountRow}>
<TextInput
editable={!this.state.sendTipStarted}
ref={ref => (this.tipAmountInput = ref)}
onChangeText={value => this.setState({ tipAmount: value })}
underlineColorAndroid={Colors.NextLbryGreen}
keyboardType={'numeric'}
onFocus={() => this.setState({ creditsInputFocused: true })}
onBlur={() => this.setState({ creditsInputFocused: false })}
placeholder={'0'}
value={this.state.tipAmount}
selectTextOnFocus
style={modalTipStyle.tipAmountInput}
/>
<Text style={modalTipStyle.currency}>LBC</Text>
<View style={modalTipStyle.balance}>
{this.state.creditsInputFocused && <Icon name="coins" size={12} />}
{this.state.creditsInputFocused && (
<Text style={modalTipStyle.balanceText}>{formatCredits(parseFloat(balance), 1, true)}</Text>
)}
</View>
</View>
{this.state.sendTipStarted && <ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />}
</View>
<View style={modalTipStyle.buttonRow}>
<Link
style={modalTipStyle.cancelTipLink}
text={__('Cancel')}
onPress={() => {
if (onCancelPress) onCancelPress();
}}
/>
<Button
text={__('Send a tip')}
style={modalTipStyle.button}
disabled={!canSendTip || this.state.sendTipStarted}
onPress={this.handleSendTip}
/>
</View>
</View>
</TouchableOpacity>
</TouchableOpacity>
);
}
}

View file

@ -56,7 +56,6 @@ window.__ = __;
const globalExceptionHandler = (error, isFatal) => {
if (error && NativeModules.Firebase) {
console.log(error);
NativeModules.Firebase.logException(isFatal, error.message ? error.message : 'No message', JSON.stringify(error));
}
};

View file

@ -23,6 +23,7 @@ import EmptyStateView from 'component/emptyStateView';
import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link';
import ModalPicker from 'component/modalPicker';
import ModalTipView from 'component/modalTipView';
import PageHeader from 'component/pageHeader';
import SubscribeButton from 'component/subscribeButton';
import SubscribeNotificationButton from 'component/subscribeNotificationButton';
@ -36,6 +37,7 @@ class ChannelPage extends React.PureComponent {
autoStyle: null,
showSortPicker: false,
showTimePicker: false,
showTipView: false,
orderBy: ['release_time'], // sort by new by default
activeTab: Constants.CONTENT_TAB,
currentSortByItem: Constants.CLAIM_SEARCH_SORT_BY_ITEMS[1], // should always default to sorting channel pages by new
@ -173,6 +175,10 @@ class ChannelPage extends React.PureComponent {
}
};
onTipPressed = () => {
this.setState({ showTipView: true });
};
onSharePressed = () => {
const { claim } = this.props;
if (claim) {
@ -209,7 +215,7 @@ class ChannelPage extends React.PureComponent {
render() {
const { channels, claim, navigation, uri, drawerStack, popDrawerStack, timeItem } = this.props;
const { name, permanent_url: permanentUrl } = claim;
const { autoStyle, currentSortByItem, showSortPicker, showTimePicker } = this.state;
const { autoStyle, currentSortByItem, showSortPicker, showTimePicker, showTipView } = this.state;
const ownedChannel = channels ? channels.map(channel => channel.permanent_url).includes(permanentUrl) : false;
let thumbnailUrl,
@ -290,6 +296,12 @@ class ChannelPage extends React.PureComponent {
icon={'share-alt'}
onPress={this.onSharePressed}
/>
<Button
style={[channelPageStyle.actionButton, channelPageStyle.tipButton]}
theme={'light'}
icon={'gift'}
onPress={this.onTipPressed}
/>
{!ownedChannel && <SubscribeButton style={channelPageStyle.subscribeButton} uri={fullUri} name={name} />}
{false && !ownedChannel && (
<SubscribeNotificationButton
@ -340,6 +352,14 @@ class ChannelPage extends React.PureComponent {
items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/>
)}
{showTipView && (
<ModalTipView
claim={claim}
onCancelPress={() => this.setState({ showTipView: false })}
onOverlayPress={() => this.setState({ showTipView: false })}
onSendTipSuccessful={() => this.setState({ showTipView: false })}
/>
)}
</View>
);
}

View file

@ -176,6 +176,9 @@ const channelPageStyle = StyleSheet.create({
marginLeft: 8,
marginRight: 8,
},
tipButton: {
marginRight: 8,
},
});
export default channelPageStyle;

58
src/styles/modalTip.js Normal file
View file

@ -0,0 +1,58 @@
import { StyleSheet } from 'react-native';
import Colors from './colors';
const modalTipStyle = StyleSheet.create({
container: {
padding: 16,
},
title: {
fontFamily: 'Inter-UI-Regular',
fontSize: 24,
},
row: {
flexDirection: 'row',
flex: 1,
},
amountRow: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 24,
},
tipAmountInput: {
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
alignSelf: 'flex-start',
textAlign: 'right',
width: 80,
letterSpacing: 1,
},
currency: {
fontFamily: 'Inter-UI-Regular',
fontSize: 12,
marginLeft: 4,
},
buttonRow: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 16,
},
button: {
backgroundColor: Colors.LbryGreen,
},
cancelTipLink: {
color: Colors.Grey,
},
balance: {
alignItems: 'center',
flexDirection: 'row',
marginLeft: 24,
},
balanceText: {
fontFamily: 'Inter-UI-SemiBold',
fontSize: 14,
marginLeft: 4,
},
});
export default modalTipStyle;