edit published content

This commit is contained in:
Akinwale Ariwodola 2019-08-22 15:51:57 +01:00
parent 1e31d8d22b
commit 330997bc1e
11 changed files with 182 additions and 45 deletions

6
package-lock.json generated
View file

@ -8046,9 +8046,9 @@
}
},
"react-native-document-picker": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-2.3.0.tgz",
"integrity": "sha512-bHMyAOzFl+II0ZdfzobKsZKvTErmXfmQGalpxpGbeN8+/uhfhUcdp4WuIMecZhFyX6rbj3h3XXLdA12hVlGgmw=="
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-3.2.4.tgz",
"integrity": "sha512-5l0/fkgasUZdIk9jUUkReDtNCQn2yg1+BrMPHMt45c/NVmE15ThnhIuDj8/n8h1F1RlhUb3SzF86ANK4OdZAiQ=="
},
"react-native-exception-handler": {
"version": "2.9.0",

View file

@ -22,7 +22,7 @@
"@react-native-community/async-storage": "^1.5.1",
"react-native-camera": "^2.11.1",
"react-native-country-picker-modal": "^0.6.2",
"react-native-document-picker": "^2.3.0",
"react-native-document-picker": "^3.2.4",
"react-native-exception-handler": "2.9.0",
"react-native-fast-image": "^6.1.1",
"react-native-fs": "^2.13.3",

View file

@ -26,12 +26,19 @@ export default class ChannelSelector extends React.PureComponent {
}
componentDidMount() {
const { channels, fetchChannelListMine, fetchingChannels } = this.props;
const { channels, channelName, fetchChannelListMine, fetchingChannels } = this.props;
if (!channels.length && !fetchingChannels) {
fetchChannelListMine();
}
}
componentDidUpdate() {
const { channelName } = this.props;
if (this.state.currentSelectedValue !== channelName) {
this.setState({ currentSelectedValue: channelName });
}
}
handleCreateCancel = () => {
this.setState({ showCreateChannel: false, newChannelName: '', newChannelBid: 0.1 });
};

View file

@ -135,9 +135,11 @@ class UriBar extends React.PureComponent {
render() {
const {
allowEdit,
belowOverlay,
navigation,
onExitSelectionMode,
onEditActionPressed,
onDeleteActionPressed,
query,
selectedItemCount,
@ -175,6 +177,19 @@ class UriBar extends React.PureComponent {
</View>
<View style={uriBarStyle.selectionModeActions}>
{allowEdit && selectedItemCount === 1 && (
<TouchableOpacity
style={[uriBarStyle.actionTouchArea, uriBarStyle.leftAction]}
onPress={() => {
if (onEditActionPressed) {
onEditActionPressed();
}
}}
>
<Icon name="edit" size={20} style={uriBarStyle.actionIcon} />
</TouchableOpacity>
)}
<TouchableOpacity
style={uriBarStyle.actionTouchArea}
onPress={() => {

View file

@ -43,6 +43,7 @@ import thunk from 'redux-thunk';
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

@ -1,6 +1,7 @@
import { connect } from 'react-redux';
import {
doFetchFileInfo,
doFetchClaimListMine,
doFileGet,
doPurchaseUri,
doDeletePurchasedUri,
@ -18,6 +19,7 @@ import {
makeSelectThumbnailForUri,
makeSelectTitleForUri,
selectBalance,
selectMyClaimUrisWithoutChannels,
selectPurchasedUris,
selectFailedPurchaseUris,
selectPurchaseUriErrorMessage,
@ -50,14 +52,15 @@ const select = (state, props) => {
contentType: makeSelectContentTypeForUri(selectProps.uri)(state),
costInfo: makeSelectCostInfoForUri(selectProps.uri)(state),
metadata: makeSelectMetadataForUri(selectProps.uri)(state),
//obscureNsfw: !selectShowNsfw(state),
//tab: makeSelectCurrentParam('tab')(state),
// obscureNsfw: !selectShowNsfw(state),
// tab: makeSelectCurrentParam('tab')(state),
fileInfo: makeSelectFileInfoForUri(selectProps.uri)(state),
rewardedContentClaimIds: selectRewardContentClaimIds(state, selectProps),
channelUri: makeSelectChannelForClaimUri(selectProps.uri, true)(state),
position: makeSelectContentPositionForUri(selectProps.uri)(state),
purchasedUris: selectPurchasedUris(state),
failedPurchaseUris: selectFailedPurchaseUris(state),
myClaimUris: selectMyClaimUrisWithoutChannels(state),
purchaseUriErrorMessage: selectPurchaseUriErrorMessage(state),
streamingUrl: makeSelectStreamingUrlForUri(selectProps.uri)(state),
thumbnail: makeSelectThumbnailForUri(selectProps.uri)(state),
@ -71,6 +74,7 @@ const perform = dispatch => ({
},
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
fetchMyClaims: () => dispatch(doFetchClaimListMine()),
fileGet: (uri, saveFile) => dispatch(doFileGet(uri, saveFile)),
notify: data => dispatch(doToast(data)),
popDrawerStack: () => dispatch(doPopDrawerStack()),

View file

@ -231,6 +231,11 @@ class FilePage extends React.PureComponent {
}
};
onEditPressed = () => {
const { claim, navigation } = this.props;
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_PUBLISH, params: { editMode: true, claimToEdit: claim } });
};
onDeletePressed = () => {
const { claim, deleteFile, deletePurchasedUri, fileInfo, navigation } = this.props;
@ -243,11 +248,11 @@ class FilePage extends React.PureComponent {
text: 'Yes',
onPress: () => {
const { uri } = navigation.state.params;
deleteFile(`${claim.txid}:${claim.nout}`, true);
deletePurchasedUri(uri);
if (NativeModules.UtilityModule) {
NativeModules.UtilityModule.deleteDownload(uri);
}
this.setState({
downloadPressed: false,
fileViewLogged: false,
@ -568,6 +573,7 @@ class FilePage extends React.PureComponent {
rewardedContentClaimIds,
isResolvingUri,
blackListedOutpoints,
myClaimUris,
navigation,
position,
purchaseUri,
@ -640,19 +646,20 @@ class FilePage extends React.PureComponent {
const isPlayable = mediaType === 'video' || mediaType === 'audio';
const { height, signing_channel: signingChannel, value } = claim;
const channelName = signingChannel && signingChannel.name;
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
const canSendTip = this.state.tipAmount > 0;
const fullUri = `${claim.name}#${claim.claim_id}`;
const canEdit = myClaimUris.includes(normalizeURI(fullUri));
const showActions =
fileInfo &&
fileInfo.download_path &&
(canEdit || (fileInfo && fileInfo.download_path)) &&
!this.state.fullscreenMode &&
!this.state.showImageViewer &&
!this.state.showWebView;
const showFileActions =
fileInfo &&
canEdit ||
(fileInfo &&
fileInfo.download_path &&
(completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
const canSendTip = this.state.tipAmount > 0;
const fullUri = `${claim.name}#${claim.claim_id}`;
(completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes)));
const fullChannelUri =
channelClaimId && channelClaimId.trim().length > 0
? normalizeURI(`${channelName}#${channelClaimId}`)
@ -823,6 +830,16 @@ class FilePage extends React.PureComponent {
<View style={filePageStyle.actions}>
{showFileActions && (
<View style={filePageStyle.fileActions}>
{canEdit && (
<Button
style={[filePageStyle.actionButton, filePageStyle.editButton]}
theme={'light'}
icon={'edit'}
text={'Edit'}
onPress={this.onEditPressed}
/>
)}
{completed && (
<Button
style={filePageStyle.actionButton}

View file

@ -18,15 +18,16 @@ import { FlatGrid } from 'react-native-super-grid';
import {
isNameValid,
buildURI,
normalizeURI,
regexInvalidURI,
CLAIM_VALUES,
LICENSES,
MATURE_TAGS,
THUMBNAIL_STATUSES,
} from 'lbry-redux';
import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker';
import { RNCamera } from 'react-native-camera';
import { generateCombination } from 'gfycat-style-urls';
import DocumentPicker from 'react-native-document-picker';
import RNFS from 'react-native-fs';
import Button from 'component/button';
import ChannelSelector from 'component/channelSelector';
@ -77,6 +78,7 @@ class PublishPage extends React.PureComponent {
state = {
canUseCamera: false,
editMode: false,
titleFocused: false,
descriptionFocused: false,
loadingVideos: false,
@ -114,7 +116,6 @@ class PublishPage extends React.PureComponent {
license: LICENSES.NONE,
licenseUrl: '',
otherLicenseDescription: '',
mature: false,
name: null,
price: 0,
uri: null,
@ -158,7 +159,7 @@ class PublishPage extends React.PureComponent {
};
onComponentFocused = () => {
const { pushDrawerStack, setPlayerVisible } = this.props;
const { pushDrawerStack, setPlayerVisible, navigation } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Publish');
@ -173,6 +174,69 @@ class PublishPage extends React.PureComponent {
NativeModules.Gallery.getVideos().then(videos => this.setState({ videos, loadingVideos: false }));
}
);
// Check if this is an edit action
if (navigation.state.params) {
const { editMode, claimToEdit } = navigation.state.params;
if (editMode) {
this.prepareEdit(claimToEdit);
}
}
};
prepareEdit = claim => {
let channelName;
const { amount, name, signing_channel: signingChannel, value } = claim;
const { description, fee, languages, license, license_url: licenseUrl, tags, thumbnail, title } = value;
if (signingChannel) {
channelName = signingChannel.name;
}
const thumbnailUrl = thumbnail ? thumbnail.url : null;
// Determine the license
let licenseType, otherLicenseDescription;
if (!LICENSES.CC_LICENSES.some(({ value }) => value === license)) {
if (!license || license === LICENSES.NONE || license === LICENSES.PUBLIC_DOMAIN) {
licenseType = license;
} else if (license && !licenseUrl && license !== LICENSES.NONE) {
licenseType = LICENSES.COPYRIGHT;
} else {
licenseType = LICENSES.OTHER;
}
otherLicenseDescription = license;
} else {
licenseType = license;
}
this.setState(
{
editMode: true,
currentPhase: Constants.PHASE_DETAILS,
bid: amount,
channelName,
description,
language: languages && languages.length > 0 ? languages[0] : 'en', // default to English
license: licenseType,
licenseUrl,
otherLicenseDescription,
name,
price: fee && fee.amount ? fee.amount : 0,
priceSet: fee && fee.amount > 0,
tags: tags && tags.length > 0 ? tags : [],
title,
currentThumbnailUri: thumbnailUrl,
uploadedThumbnailUri: thumbnailUrl,
},
() => {
this.handleNameChange(name);
if (channelName) {
this.handleChannelChange(channelName);
}
}
);
};
getNewUri(name, channel) {
@ -209,6 +273,7 @@ class PublishPage extends React.PureComponent {
handlePublishPressed = () => {
const { notify, publish, updatePublishForm } = this.props;
const {
editMode,
bid,
channelName,
currentMedia,
@ -217,7 +282,6 @@ class PublishPage extends React.PureComponent {
license,
licenseUrl,
otherLicenseDescription,
mature,
name,
price,
priceSet,
@ -237,14 +301,21 @@ class PublishPage extends React.PureComponent {
return;
}
if (!currentMedia && !editMode) {
// sanity check. normally shouldn't happen
notify({ message: 'No file selected. Please select a video or take a photo before publishing.' });
return;
}
const publishParams = {
filePath: currentMedia.filePath,
filePath: currentMedia ? currentMedia.filePath : null,
bid: bid || 0.1,
title: title || '',
thumbnail,
description: description || '',
language,
license,
licenseType: license,
licenseUrl,
otherLicenseDescription,
name: name || undefined,
@ -253,12 +324,9 @@ class PublishPage extends React.PureComponent {
uri: uri || undefined,
channel: CLAIM_VALUES.CHANNEL_ANONYMOUS === channelName ? null : channelName,
isStillEditing: false,
claim: {
value: {
tags,
release_time: Math.round(Date.now() / 1000), // set now as the release time
},
},
tags: tags.map(tag => {
return { name: tag };
}),
};
updatePublishForm(publishParams);
@ -320,6 +388,7 @@ class PublishPage extends React.PureComponent {
this.setState(
{
publishStarted: false,
editMode: false,
currentMedia: null,
currentThumbnailUri: null,
@ -434,16 +503,16 @@ class PublishPage extends React.PureComponent {
};
handleUploadPressed = () => {
DocumentPicker.show(
{
filetype: [DocumentPickerUtil.allFiles()],
},
(error, res) => {
if (!error) {
// console.log(res);
DocumentPicker.pick({ type: [DocumentPicker.types.allFiles] })
.then(file => {
// console.log(file);
})
.catch(error => {
if (!DocumentPicker.isCancel(error)) {
// notify the user
// console.log(error);
}
}
);
});
};
getRandomFileId = () => {
@ -685,9 +754,12 @@ class PublishPage extends React.PureComponent {
)}
</View>
);
} else if (Constants.PHASE_DETAILS === this.state.currentPhase && this.state.currentMedia) {
} else if (
Constants.PHASE_DETAILS === this.state.currentPhase &&
(this.state.editMode || this.state.currentMedia)
) {
const { currentMedia, currentThumbnailUri } = this.state;
if (!currentThumbnailUri) {
if (!currentThumbnailUri && !this.state.editMode) {
this.updateThumbnailUriForMedia(currentMedia);
}
content = (
@ -764,7 +836,7 @@ class PublishPage extends React.PureComponent {
<View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Channel</Text>
<ChannelSelector onChannelChange={this.handleChannelChange} />
<ChannelSelector channelName={this.state.channelName} onChannelChange={this.handleChannelChange} />
</View>
<View style={publishStyle.card}>
@ -798,11 +870,14 @@ class PublishPage extends React.PureComponent {
<View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Content Address</Text>
<Text style={publishStyle.helpText}>
The address where people can find your content (ex. lbry://myvideo)
The address where people can find your content (ex. lbry://myvideo).
{this.state.editMode &&
' You cannot change this address while editing your content. If you wish to use a new address, please republish the content.'}
</Text>
<TextInput
placeholder={'lbry://'}
editable={!this.state.editMode}
style={publishStyle.inputText}
underlineColorAndroid={Colors.NextLbryGreen}
numberOfLines={1}
@ -902,7 +977,7 @@ class PublishPage extends React.PureComponent {
<Button
style={publishStyle.publishButton}
disabled={balance < 0.1 || !this.state.uploadedThumbnailUri}
text="Publish"
text={this.state.editMode ? 'Save changes' : 'Publish'}
onPress={this.handlePublishPressed}
/>
</View>

View file

@ -70,6 +70,16 @@ class PublishesPage extends React.PureComponent {
this.setState({ selectionMode: false, selectedUris: [], selectedClaimsMap: {} });
};
onEditActionPressed = () => {
const { navigation } = this.props;
const { selectedClaimsMap, selectedUris } = this.state;
// only 1 item can be edited (and edit button should be visible only if there is a single selection)
const claim = selectedClaimsMap[selectedUris[0]];
this.onExitSelectionMode();
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_PUBLISH, params: { editMode: true, claimToEdit: claim } });
};
onDeleteActionPressed = () => {
const { abandonClaim } = this.props;
const { selectedClaimsMap } = this.state;
@ -103,11 +113,13 @@ class PublishesPage extends React.PureComponent {
return (
<View style={publishStyle.container}>
<UriBar
allowEdit
navigation={navigation}
selectionMode={selectionMode}
selectedItemCount={selectedUris.length}
onExitSelectionMode={this.onExitSelectionMode}
onDeleteActionPressed={this.onDeleteActionPressed}
onEditActionPressed={this.onEditActionPressed}
onExitSelectionMode={this.onExitSelectionMode}
/>
{fetching && (
<View style={publishStyle.centered}>
@ -138,7 +150,6 @@ class PublishesPage extends React.PureComponent {
removeClippedSubviews
renderItem={({ item }) => (
<FileListItem
hideChannel
key={item}
uri={item}
style={publishStyle.listItem}

View file

@ -194,6 +194,10 @@ const filePageStyle = StyleSheet.create({
},
fileActions: {
alignSelf: 'flex-end',
flexDirection: 'row',
},
editButton: {
marginRight: 8,
},
socialActions: {
alignSelf: 'flex-start',

View file

@ -98,6 +98,9 @@ const uriBarStyle = StyleSheet.create({
height: '100%',
alignItems: 'center',
},
leftAction: {
marginRight: 16,
},
actionTouchArea: {
height: '100%',
alignItems: 'center',