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

View file

@ -1,12 +1,12 @@
// @flow
import React from 'react';
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 Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import UriBarItem from './internal/uri-bar-item';
import Icon from 'react-native-vector-icons/FontAwesome5';
import NavigationButton from 'component/navigationButton';
import discoverStyle from 'styles/discover';
import uriBarStyle from 'styles/uriBar';
class UriBar extends React.PureComponent {
@ -16,6 +16,16 @@ class UriBar extends React.PureComponent {
keyboardDidHideListener = null;
state = {
changeTextTimeout: null,
currentValue: null,
inputText: null,
focused: false,
// TODO: Add a setting to enable / disable direct search?
directSearch: true,
};
componentDidMount() {
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
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 => {
const newValue = text || '';
clearTimeout(this.state.changeTextTimeout);
@ -136,7 +134,17 @@ class UriBar extends React.PureComponent {
}
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) {
this.setState({ currentValue: value });
}
@ -146,42 +154,78 @@ class UriBar extends React.PureComponent {
// TODO: Add optional setting to enable URI / search bar suggestions
/* if (this.state.focused) { style.push(uriBarStyle.inFocus); } */
// TODO: selectionModeActions should be dynamically created / specified
return (
<View style={style}>
<View style={[uriBarStyle.uriContainer, belowOverlay ? null : uriBarStyle.containerElevated]}>
<NavigationButton
name="bars"
size={24}
style={uriBarStyle.drawerMenuButton}
iconStyle={discoverStyle.drawerHamburger}
onPress={() => navigation.openDrawer()}
/>
<TextInput
ref={ref => {
this.textInput = ref;
}}
autoCorrect={false}
style={uriBarStyle.uriText}
onLayout={() => {
this.setSelection();
}}
selectTextOnFocus
placeholder={'Search movies, music, and more'}
underlineColorAndroid={'transparent'}
numberOfLines={1}
clearButtonMode={'while-editing'}
value={this.state.currentValue}
returnKeyType={'go'}
inlineImageLeft={'baseline_search_black_24'}
inlineImagePadding={16}
onFocus={() => this.setState({ focused: true })}
onBlur={() => {
this.setState({ focused: false });
this.setSelection();
}}
onChangeText={this.handleChangeText}
onSubmitEditing={this.handleSubmitEditing}
/>
{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
name="bars"
size={24}
style={uriBarStyle.drawerMenuButton}
iconStyle={uriBarStyle.drawerHamburger}
onPress={() => navigation.openDrawer()}
/>
)}
{!selectionMode && (
<TextInput
ref={ref => {
this.textInput = ref;
}}
autoCorrect={false}
style={uriBarStyle.uriText}
onLayout={() => {
this.setSelection();
}}
selectTextOnFocus
placeholder={'Search movies, music, and more'}
underlineColorAndroid={'transparent'}
numberOfLines={1}
clearButtonMode={'while-editing'}
value={this.state.currentValue}
returnKeyType={'go'}
inlineImageLeft={'baseline_search_black_24'}
inlineImagePadding={16}
onFocus={() => this.setState({ focused: true })}
onBlur={() => {
this.setState({ focused: false });
this.setSelection();
}}
onChangeText={this.handleChangeText}
onSubmitEditing={this.handleSubmitEditing}
/>
)}
{this.state.focused && !this.state.directSearch && (
<View style={uriBarStyle.suggestions}>
<FlatList

View file

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

View file

@ -1,5 +1,5 @@
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 Colors from 'styles/colors';
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 UriBar from 'component/uriBar';
import publishStyle from 'styles/publish';
import { __ } from 'utils/helper';
import { __, navigateToUri } from 'utils/helper';
class PublishesPage extends React.PureComponent {
state = {
selectionMode: false,
selectedUris: [],
selectedClaimsMap: {},
};
didFocusListener;
componentWillMount() {
@ -36,12 +42,67 @@ class PublishesPage extends React.PureComponent {
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() {
const { fetching, navigation, uris } = this.props;
const { selectionMode, selectedUris } = this.state;
return (
<View style={publishStyle.container}>
<UriBar navigation={navigation} />
<UriBar
navigation={navigation}
selectionMode={selectionMode}
selectedItemCount={selectedUris.length}
onExitSelectionMode={this.onExitSelectionMode}
onDeleteActionPressed={this.onDeleteActionPressed}
/>
{fetching && (
<View style={publishStyle.centered}>
<ActivityIndicator size={'small'} color={Colors.LbryGreen} />
@ -65,11 +126,28 @@ class PublishesPage extends React.PureComponent {
<FlatList
style={publishStyle.publishesList}
contentContainerStyle={publishStyle.publishesScrollPadding}
extraData={this.state}
initialNumToRender={8}
maxToRenderPerBatch={24}
removeClippedSubviews
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}
keyExtractor={(item, index) => item}

View file

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

View file

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

View file

@ -20,6 +20,10 @@ const uriBarStyle = StyleSheet.create({
containerElevated: {
elevation: 4,
},
drawerHamburger: {
marginLeft: 16,
marginRight: 16,
},
uriText: {
backgroundColor: Colors.VeryLightGrey,
borderRadius: 24,
@ -73,6 +77,36 @@ const uriBarStyle = StyleSheet.create({
justifyContent: 'center',
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;