list selection mode and deleting published items (#26)

This commit is contained in:
Akinwale Ariwodola 2019-08-16 18:26:13 +01:00 committed by GitHub
parent 2c6ac47d1a
commit 41f511e233
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 237 additions and 61 deletions

View file

@ -46,6 +46,15 @@ class FileListItem extends React.PureComponent {
navigateToUri(navigation, shortUrl || uri, { autoplay }); navigateToUri(navigation, shortUrl || uri, { autoplay });
}; };
onPressHandler = () => {
const { claim, onPress } = this.props;
if (onPress) {
onPress(claim);
} else {
this.defaultOnPress();
}
};
render() { render() {
const { const {
blackListedOutpoints, blackListedOutpoints,
@ -63,6 +72,8 @@ class FileListItem extends React.PureComponent {
navigation, navigation,
thumbnail, thumbnail,
hideChannel, hideChannel,
onLongPress,
selected,
title, title,
} = this.props; } = this.props;
@ -95,7 +106,7 @@ class FileListItem extends React.PureComponent {
return ( return (
<View style={style}> <View style={style}>
<TouchableOpacity style={style} onPress={onPress || this.defaultOnPress}> <TouchableOpacity style={style} onPress={this.onPressHandler} onLongPress={() => onLongPress(claim)}>
<FileItemMedia <FileItemMedia
style={fileListStyle.thumbnail} style={fileListStyle.thumbnail}
blurRadius={obscure ? 15 : 0} blurRadius={obscure ? 15 : 0}
@ -103,6 +114,11 @@ class FileListItem extends React.PureComponent {
title={title || name} title={title || name}
thumbnail={thumbnail} thumbnail={thumbnail}
/> />
{selected && (
<View style={fileListStyle.selectedOverlay}>
<Icon name={'check-circle'} solid color={Colors.NextLbryGreen} size={32} />
</View>
)}
{fileInfo && fileInfo.completed && fileInfo.download_path && ( {fileInfo && fileInfo.completed && fileInfo.download_path && (
<Icon style={fileListStyle.downloadedIcon} solid color={Colors.NextLbryGreen} name={'folder'} size={16} /> <Icon style={fileListStyle.downloadedIcon} solid color={Colors.NextLbryGreen} name={'folder'} size={16} />
)} )}

View file

@ -1,12 +1,12 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { SEARCH_TYPES, isNameValid, isURIValid, normalizeURI } from 'lbry-redux'; import { SEARCH_TYPES, isNameValid, isURIValid, normalizeURI } from 'lbry-redux';
import { FlatList, Keyboard, TextInput, View } from 'react-native'; import { FlatList, Keyboard, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { navigateToUri } from 'utils/helper'; import { navigateToUri } from 'utils/helper';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import UriBarItem from './internal/uri-bar-item'; import UriBarItem from './internal/uri-bar-item';
import Icon from 'react-native-vector-icons/FontAwesome5';
import NavigationButton from 'component/navigationButton'; import NavigationButton from 'component/navigationButton';
import discoverStyle from 'styles/discover';
import uriBarStyle from 'styles/uriBar'; import uriBarStyle from 'styles/uriBar';
class UriBar extends React.PureComponent { class UriBar extends React.PureComponent {
@ -16,6 +16,16 @@ class UriBar extends React.PureComponent {
keyboardDidHideListener = null; keyboardDidHideListener = null;
state = {
changeTextTimeout: null,
currentValue: null,
inputText: null,
focused: false,
// TODO: Add a setting to enable / disable direct search?
directSearch: true,
};
componentDidMount() { componentDidMount() {
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide); this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
this.setSelection(); this.setSelection();
@ -36,18 +46,6 @@ class UriBar extends React.PureComponent {
} }
} }
constructor(props) {
super(props);
this.state = {
changeTextTimeout: null,
currentValue: null,
inputText: null,
focused: false,
// TODO: Add a setting to enable / disable direct search?
directSearch: true,
};
}
handleChangeText = text => { handleChangeText = text => {
const newValue = text || ''; const newValue = text || '';
clearTimeout(this.state.changeTextTimeout); clearTimeout(this.state.changeTextTimeout);
@ -136,7 +134,17 @@ class UriBar extends React.PureComponent {
} }
render() { render() {
const { navigation, suggestions, query, value, belowOverlay } = this.props; const {
belowOverlay,
navigation,
onExitSelectionMode,
onDeleteActionPressed,
query,
selectedItemCount,
selectionMode,
suggestions,
value,
} = this.props;
if (this.state.currentValue === null) { if (this.state.currentValue === null) {
this.setState({ currentValue: value }); this.setState({ currentValue: value });
} }
@ -146,16 +154,51 @@ class UriBar extends React.PureComponent {
// TODO: Add optional setting to enable URI / search bar suggestions // TODO: Add optional setting to enable URI / search bar suggestions
/* if (this.state.focused) { style.push(uriBarStyle.inFocus); } */ /* if (this.state.focused) { style.push(uriBarStyle.inFocus); } */
// TODO: selectionModeActions should be dynamically created / specified
return ( return (
<View style={style}> <View style={style}>
<View style={[uriBarStyle.uriContainer, belowOverlay ? null : uriBarStyle.containerElevated]}> <View style={[uriBarStyle.uriContainer, belowOverlay ? null : uriBarStyle.containerElevated]}>
{selectionMode && (
<View style={uriBarStyle.selectionModeBar}>
<View style={uriBarStyle.selectionModeLeftBar}>
<TouchableOpacity
style={uriBarStyle.backTouchArea}
onPress={() => {
if (onExitSelectionMode) {
onExitSelectionMode();
}
}}
>
<Icon name="arrow-left" size={20} />
</TouchableOpacity>
{selectedItemCount > 0 && <Text style={uriBarStyle.itemCount}>{selectedItemCount}</Text>}
</View>
<View style={uriBarStyle.selectionModeActions}>
<TouchableOpacity
style={uriBarStyle.actionTouchArea}
onPress={() => {
if (onDeleteActionPressed) {
onDeleteActionPressed();
}
}}
>
<Icon name="trash-alt" solid={false} size={20} style={uriBarStyle.actionIcon} />
</TouchableOpacity>
</View>
</View>
)}
{!selectionMode && (
<NavigationButton <NavigationButton
name="bars" name="bars"
size={24} size={24}
style={uriBarStyle.drawerMenuButton} style={uriBarStyle.drawerMenuButton}
iconStyle={discoverStyle.drawerHamburger} iconStyle={uriBarStyle.drawerHamburger}
onPress={() => navigation.openDrawer()} onPress={() => navigation.openDrawer()}
/> />
)}
{!selectionMode && (
<TextInput <TextInput
ref={ref => { ref={ref => {
this.textInput = ref; this.textInput = ref;
@ -182,6 +225,7 @@ class UriBar extends React.PureComponent {
onChangeText={this.handleChangeText} onChangeText={this.handleChangeText}
onSubmitEditing={this.handleSubmitEditing} onSubmitEditing={this.handleSubmitEditing}
/> />
)}
{this.state.focused && !this.state.directSearch && ( {this.state.focused && !this.state.directSearch && (
<View style={uriBarStyle.suggestions}> <View style={uriBarStyle.suggestions}>
<FlatList <FlatList

View file

@ -1,5 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
doAbandonClaim,
doCheckPendingPublishes, doCheckPendingPublishes,
doFetchClaimListMine, doFetchClaimListMine,
selectMyClaimUrisWithoutChannels, selectMyClaimUrisWithoutChannels,
@ -15,6 +16,7 @@ const select = state => ({
}); });
const perform = dispatch => ({ const perform = dispatch => ({
abandonClaim: (txid, nout) => dispatch(doAbandonClaim(txid, nout)),
fetchMyClaims: () => dispatch(doFetchClaimListMine()), fetchMyClaims: () => dispatch(doFetchClaimListMine()),
checkPendingPublishes: () => dispatch(doCheckPendingPublishes()), checkPendingPublishes: () => dispatch(doCheckPendingPublishes()),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_PUBLISHES)), pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_PUBLISHES)),

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { ActivityIndicator, FlatList, Text, TouchableOpacity, View } from 'react-native'; import { ActivityIndicator, Alert, FlatList, Text, TouchableOpacity, View } from 'react-native';
import Button from 'component/button'; import Button from 'component/button';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -7,9 +7,15 @@ import FileListItem from 'component/fileListItem';
import FloatingWalletBalance from 'component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import publishStyle from 'styles/publish'; import publishStyle from 'styles/publish';
import { __ } from 'utils/helper'; import { __, navigateToUri } from 'utils/helper';
class PublishesPage extends React.PureComponent { class PublishesPage extends React.PureComponent {
state = {
selectionMode: false,
selectedUris: [],
selectedClaimsMap: {},
};
didFocusListener; didFocusListener;
componentWillMount() { componentWillMount() {
@ -36,12 +42,67 @@ class PublishesPage extends React.PureComponent {
checkPendingPublishes(); checkPendingPublishes();
}; };
addOrRemoveItem = (uri, claim) => {
const { selectedClaimsMap } = this.state;
let selectedUris = [...this.state.selectedUris];
if (selectedUris.includes(uri)) {
delete selectedClaimsMap[uri];
selectedUris.splice(selectedUris.indexOf(uri), 1);
} else {
selectedClaimsMap[uri] = claim;
selectedUris.push(uri);
}
this.setState({ selectionMode: selectedUris.length > 0, selectedUris, selectedClaimsMap });
};
handleSelectItem = (uri, claim) => {
this.addOrRemoveItem(uri, claim);
};
handleItemLongPress = (uri, claim) => {
this.addOrRemoveItem(uri, claim);
};
onExitSelectionMode = () => {
this.setState({ selectionMode: false, selectedUris: [], selectedClaimsMap: {} });
};
onDeleteActionPressed = () => {
const { abandonClaim } = this.props;
const { selectedClaimsMap } = this.state;
// show confirm alert
Alert.alert(__('Unpublish'), __('Are you sure you want to unpublish the selected content?'), [
{ text: __('No') },
{
text: __('Yes'),
onPress: () => {
const uris = Object.keys(selectedClaimsMap);
uris.forEach(uri => {
const { txid, nout } = selectedClaimsMap[uri];
abandonClaim(txid, nout);
});
this.onExitSelectionMode();
},
},
]);
};
render() { render() {
const { fetching, navigation, uris } = this.props; const { fetching, navigation, uris } = this.props;
const { selectionMode, selectedUris } = this.state;
return ( return (
<View style={publishStyle.container}> <View style={publishStyle.container}>
<UriBar navigation={navigation} /> <UriBar
navigation={navigation}
selectionMode={selectionMode}
selectedItemCount={selectedUris.length}
onExitSelectionMode={this.onExitSelectionMode}
onDeleteActionPressed={this.onDeleteActionPressed}
/>
{fetching && ( {fetching && (
<View style={publishStyle.centered}> <View style={publishStyle.centered}>
<ActivityIndicator size={'small'} color={Colors.LbryGreen} /> <ActivityIndicator size={'small'} color={Colors.LbryGreen} />
@ -65,11 +126,28 @@ class PublishesPage extends React.PureComponent {
<FlatList <FlatList
style={publishStyle.publishesList} style={publishStyle.publishesList}
contentContainerStyle={publishStyle.publishesScrollPadding} contentContainerStyle={publishStyle.publishesScrollPadding}
extraData={this.state}
initialNumToRender={8} initialNumToRender={8}
maxToRenderPerBatch={24} maxToRenderPerBatch={24}
removeClippedSubviews removeClippedSubviews
renderItem={({ item }) => ( renderItem={({ item }) => (
<FileListItem hideChannel key={item} uri={item} style={publishStyle.listItem} navigation={navigation} /> <FileListItem
hideChannel
key={item}
uri={item}
style={publishStyle.listItem}
selected={selectedUris.includes(item)}
onPress={claim => {
if (selectionMode) {
this.handleSelectItem(item, claim);
} else {
// TODO: when shortUrl is available for my claims, navigate to that URL instead
navigateToUri(navigation, item);
}
}}
onLongPress={claim => this.handleItemLongPress(item, claim)}
navigation={navigation}
/>
)} )}
data={uris} data={uris}
keyExtractor={(item, index) => item} keyExtractor={(item, index) => item}

View file

@ -167,14 +167,6 @@ const discoverStyle = StyleSheet.create({
textAlign: 'center', textAlign: 'center',
color: '#0c604b', color: '#0c604b',
}, },
drawerMenuButton: {
height: '100%',
justifyContent: 'center',
},
drawerHamburger: {
marginLeft: 16,
marginRight: 16,
},
rightHeaderIcon: { rightHeaderIcon: {
marginRight: 16, marginRight: 16,
}, },

View file

@ -38,6 +38,16 @@ const fileListStyle = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}, },
selectedOverlay: {
position: 'absolute',
left: 0,
top: 0,
width: thumbnailWidth,
height: thumbnailHeight,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#000000aa',
},
title: { title: {
fontFamily: 'Inter-UI-SemiBold', fontFamily: 'Inter-UI-SemiBold',
fontSize: screenWidthPixels <= 720 ? 12 : 14, fontSize: screenWidthPixels <= 720 ? 12 : 14,

View file

@ -20,6 +20,10 @@ const uriBarStyle = StyleSheet.create({
containerElevated: { containerElevated: {
elevation: 4, elevation: 4,
}, },
drawerHamburger: {
marginLeft: 16,
marginRight: 16,
},
uriText: { uriText: {
backgroundColor: Colors.VeryLightGrey, backgroundColor: Colors.VeryLightGrey,
borderRadius: 24, borderRadius: 24,
@ -73,6 +77,36 @@ const uriBarStyle = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
flex: 3, flex: 3,
}, },
selectionModeBar: {
flex: 1,
height: 46,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
selectionModeLeftBar: {
flexDirection: 'row',
alignItems: 'center',
marginLeft: 16,
},
selectionModeActions: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 16,
},
backTouchArea: {
height: '100%',
alignItems: 'center',
},
actionTouchArea: {
height: '100%',
alignItems: 'center',
},
itemCount: {
fontFamily: 'Inter-UI-Regular',
fontSize: 20,
marginLeft: 30,
},
}); });
export default uriBarStyle; export default uriBarStyle;