Prevent concurrent uploads with same lbry name

## Ticket
426

## Issue
Currently, we check if we have any existing claims with the same name when uploading, i.e. "lbry://<name>". It does not include claims that you are still uploading, so you might end up with duplicate claims.

In the ticket, there is also the issue of 2 uploads sharing the same slot, causing the progress indicator to jumpy between the uploads. That has been fixed by using a guid instead of using `name`.

## Aside
I think there is another request to allow the same name but on different channel ... next time, next time ....
This commit is contained in:
infinite-persistence 2021-12-10 20:27:04 +08:00 committed by Thomas Zarebczan
parent cb78d568c5
commit 224f10663d
4 changed files with 29 additions and 1 deletions

View file

@ -1206,6 +1206,7 @@
"YB": "YB", "YB": "YB",
"Edit existing claim instead": "Edit existing claim instead", "Edit existing claim instead": "Edit existing claim instead",
"You already have a claim at %existing_uri%. Publishing will update (overwrite) your existing claim.": "You already have a claim at %existing_uri%. Publishing will update (overwrite) your existing claim.", "You already have a claim at %existing_uri%. Publishing will update (overwrite) your existing claim.": "You already have a claim at %existing_uri%. Publishing will update (overwrite) your existing claim.",
"You already have a pending upload at %existing_uri%.": "You already have a pending upload at %existing_uri%.",
"Save": "Save", "Save": "Save",
"Saved": "Saved", "Saved": "Saved",
"Saving...": "Saving...", "Saving...": "Saving...",

View file

@ -5,6 +5,7 @@ import {
selectIsStillEditing, selectIsStillEditing,
selectMyClaimForUri, selectMyClaimForUri,
selectTakeOverAmount, selectTakeOverAmount,
selectCurrentUploads,
} from 'redux/selectors/publish'; } from 'redux/selectors/publish';
import { selectActiveChannelClaim, selectIncognito } from 'redux/selectors/app'; import { selectActiveChannelClaim, selectIncognito } from 'redux/selectors/app';
import { doSetActiveChannel } from 'redux/actions/app'; import { doSetActiveChannel } from 'redux/actions/app';
@ -15,6 +16,7 @@ const select = (state) => ({
uri: makeSelectPublishFormValue('uri')(state), uri: makeSelectPublishFormValue('uri')(state),
isStillEditing: selectIsStillEditing(state), isStillEditing: selectIsStillEditing(state),
myClaimForUri: selectMyClaimForUri(state), myClaimForUri: selectMyClaimForUri(state),
currentUploads: selectCurrentUploads(state),
activeChannelClaim: selectActiveChannelClaim(state), activeChannelClaim: selectActiveChannelClaim(state),
incognito: selectIncognito(state), incognito: selectIncognito(state),
amountNeededForTakeover: selectTakeOverAmount(state), amountNeededForTakeover: selectTakeOverAmount(state),

View file

@ -4,19 +4,41 @@ import Button from 'component/button';
import { buildURI } from 'util/lbryURI'; import { buildURI } from 'util/lbryURI';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
function isUriPendingUpload(uri: ?string, currentUploadNames: Array<string>) {
const protocol = 'lbry://';
const uriName = uri && uri.startsWith(protocol) ? uri.substring(protocol.length) : uri;
return currentUploadNames.includes(uriName);
}
type Props = { type Props = {
uri: ?string, uri: ?string,
myClaimForUri: ?StreamClaim, myClaimForUri: ?StreamClaim,
currentUploads: { [key: string]: FileUploadItem },
isStillEditing: boolean, isStillEditing: boolean,
onEditMyClaim: (any, string) => void, onEditMyClaim: (any, string) => void,
}; };
function NameHelpText(props: Props) { function NameHelpText(props: Props) {
const { uri, myClaimForUri, onEditMyClaim, isStillEditing } = props; const { uri, myClaimForUri, currentUploads, onEditMyClaim, isStillEditing } = props;
const currentUploadNames: Array<string> = React.useMemo(() => {
// $FlowFixMe - unable to resolve mixed
return Object.values(currentUploads).map((x) => (x.params ? x.params.name : ''));
}, [currentUploads]);
let nameHelpText; let nameHelpText;
if (isStillEditing) { if (isStillEditing) {
nameHelpText = __('You are currently editing this claim.'); nameHelpText = __('You are currently editing this claim.');
} else if (isUriPendingUpload(uri, currentUploadNames)) {
nameHelpText = (
<div className="error__text">
{/* prettier-ignore */}
<I18nMessage tokens={{ existing_uri: (<u><em>{uri}</em></u>) }}>
You already have a pending upload at %existing_uri%.
</I18nMessage>
</div>
);
} else if (uri && myClaimForUri) { } else if (uri && myClaimForUri) {
const editUri = buildURI({ const editUri = buildURI({
streamName: myClaimForUri.name, streamName: myClaimForUri.name,

View file

@ -16,6 +16,7 @@ type Props = {
updatePublishForm: ({}) => void, updatePublishForm: ({}) => void,
activeChannelClaim: ?ChannelClaim, activeChannelClaim: ?ChannelClaim,
incognito: boolean, incognito: boolean,
currentUploads: { [key: string]: FileUploadItem },
}; };
function PublishName(props: Props) { function PublishName(props: Props) {
@ -28,6 +29,7 @@ function PublishName(props: Props) {
updatePublishForm, updatePublishForm,
activeChannelClaim, activeChannelClaim,
incognito, incognito,
currentUploads,
} = props; } = props;
const [nameError, setNameError] = useState(undefined); const [nameError, setNameError] = useState(undefined);
const [blurred, setBlurred] = React.useState(false); const [blurred, setBlurred] = React.useState(false);
@ -84,6 +86,7 @@ function PublishName(props: Props) {
uri={uri} uri={uri}
isStillEditing={isStillEditing} isStillEditing={isStillEditing}
myClaimForUri={myClaimForUri} myClaimForUri={myClaimForUri}
currentUploads={currentUploads}
onEditMyClaim={editExistingClaim} onEditMyClaim={editExistingClaim}
/> />
</div> </div>