list selection mode and deleting published items (#26)
This commit is contained in:
parent
2c6ac47d1a
commit
41f511e233
7 changed files with 237 additions and 61 deletions
|
@ -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} />
|
||||
)}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue