diff --git a/ui/component/claimMenuList/index.js b/ui/component/claimMenuList/index.js index acf6b99d4..30e114eaf 100644 --- a/ui/component/claimMenuList/index.js +++ b/ui/component/claimMenuList/index.js @@ -5,7 +5,6 @@ import { makeSelectFileInfoForUri, doPrepareEdit, makeSelectCollectionForIdHasClaimUrl, - makeSelectNameForCollectionId, makeSelectCollectionIsMine, COLLECTIONS_CONSTS, makeSelectEditedCollectionForId, @@ -34,20 +33,28 @@ import fs from 'fs'; const select = (state, props) => { const claim = makeSelectClaimForUri(props.uri, false)(state); - const permanentUri = claim && claim.permanent_url; + const repostedClaim = claim && claim.reposted_claim; + const contentClaim = repostedClaim || claim; + const contentSigningChannel = contentClaim && contentClaim.signing_channel; + const contentPermanentUri = contentClaim && contentClaim.permanent_url; + const contentChannelUri = (contentSigningChannel && contentSigningChannel.permanent_url) || contentPermanentUri; + return { claim, + repostedClaim, + contentClaim, + contentSigningChannel, + contentChannelUri, claimIsMine: makeSelectSigningIsMine(props.uri)(state), - hasClaimInWatchLater: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.WATCH_LATER_ID, permanentUri)(state), - hasClaimInCustom: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.FAVORITES_ID, permanentUri)(state), - channelIsMuted: makeSelectChannelIsMuted(props.uri)(state), - channelIsBlocked: makeSelectChannelIsBlocked(props.uri)(state), + hasClaimInWatchLater: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.WATCH_LATER_ID, contentPermanentUri)(state), + hasClaimInCustom: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.FAVORITES_ID, contentPermanentUri)(state), + channelIsMuted: makeSelectChannelIsMuted(contentChannelUri)(state), + channelIsBlocked: makeSelectChannelIsBlocked(contentChannelUri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state), - isSubscribed: makeSelectIsSubscribed(props.channelUri, true)(state), + isSubscribed: makeSelectIsSubscribed(contentChannelUri, true)(state), channelIsAdminBlocked: makeSelectChannelIsAdminBlocked(props.uri)(state), isAdmin: selectHasAdminChannel(state), - claimInCollection: makeSelectCollectionForIdHasClaimUrl(props.collectionId, permanentUri)(state), - collectionName: makeSelectNameForCollectionId(props.collectionId)(state), + claimInCollection: makeSelectCollectionForIdHasClaimUrl(props.collectionId, contentPermanentUri)(state), isMyCollection: makeSelectCollectionIsMine(props.collectionId)(state), editedCollection: makeSelectEditedCollectionForId(props.collectionId)(state), isAuthenticated: Boolean(selectUserVerifiedEmail(state)), @@ -71,7 +78,8 @@ const perform = (dispatch) => ({ doChannelUnmute: (channelUri) => dispatch(doChannelUnmute(channelUri)), doCommentModBlock: (channelUri) => dispatch(doCommentModBlock(channelUri)), doCommentModUnBlock: (channelUri) => dispatch(doCommentModUnBlock(channelUri)), - doCommentModBlockAsAdmin: (commenterUri, blockerId) => dispatch(doCommentModBlockAsAdmin(commenterUri, blockerId)), + doCommentModBlockAsAdmin: (commenterUri, blockerId) => + dispatch(doCommentModBlockAsAdmin(commenterUri, blockerId)), doCommentModUnBlockAsAdmin: (commenterUri, blockerId) => dispatch(doCommentModUnBlockAsAdmin(commenterUri, blockerId)), doChannelSubscribe: (subscription) => dispatch(doChannelSubscribe(subscription)), diff --git a/ui/component/claimMenuList/view.jsx b/ui/component/claimMenuList/view.jsx index bc38196f1..6220cc783 100644 --- a/ui/component/claimMenuList/view.jsx +++ b/ui/component/claimMenuList/view.jsx @@ -23,8 +23,11 @@ type SubscriptionArgs = { type Props = { uri: string, - channelUri: string, claim: ?Claim, + repostedClaim: ?Claim, + contentClaim: ?Claim, + contentSigningChannel: ?Claim, + contentChannelUri: string, openModal: (id: string, {}) => void, inline?: boolean, channelIsMuted: boolean, @@ -37,12 +40,10 @@ type Props = { doCommentModUnBlock: (string) => void, doCommentModBlockAsAdmin: (string, string) => void, doCommentModUnBlockAsAdmin: (string, string) => void, - isRepost: boolean, doCollectionEdit: (string, any) => void, hasClaimInWatchLater: boolean, hasClaimInCustom: boolean, claimInCollection: boolean, - collectionName?: string, collectionId: string, isMyCollection: boolean, doToast: ({ message: string, isError?: boolean }) => void, @@ -60,8 +61,11 @@ type Props = { function ClaimMenuList(props: Props) { const { uri, - channelUri, claim, + repostedClaim, + contentClaim, + contentSigningChannel, + contentChannelUri, openModal, inline = false, doChannelMute, @@ -72,14 +76,12 @@ function ClaimMenuList(props: Props) { isAdmin, doCommentModBlock, doCommentModUnBlock, - isRepost, doCommentModBlockAsAdmin, doCommentModUnBlockAsAdmin, doCollectionEdit, hasClaimInWatchLater, hasClaimInCustom, collectionId, - collectionName, isMyCollection, doToast, claimIsMine, @@ -92,15 +94,16 @@ function ClaimMenuList(props: Props) { editedCollection, isAuthenticated, } = props; - const repostedContent = claim && claim.reposted_claim; - const contentClaim = repostedContent || claim; - const incognitoClaim = channelUri && !channelUri.includes('@'); - const signingChannel = claim && (claim.signing_channel || claim); - const permanentUrl = String(channelUri); - const isChannel = !incognitoClaim && signingChannel === claim; + const incognitoClaim = contentChannelUri && !contentChannelUri.includes('@'); + const isChannel = !incognitoClaim && !contentSigningChannel; + const { channelName } = parseURI(contentChannelUri); const showDelete = claimIsMine || (fileInfo && (fileInfo.written_bytes > 0 || fileInfo.blobs_completed > 0)); - const subscriptionLabel = isSubscribed ? __('Unfollow') : __('Follow'); + const subscriptionLabel = __('%action%' + '%user%', { + action: isSubscribed ? __('Unfollow') : __('Follow'), + user: repostedClaim ? __(' @' + channelName) : '', + }); const lastCollectionName = 'Favorites'; + const lastCollectionId = COLLECTIONS_CONSTS.FAVORITES_ID; const { push, replace } = useHistory(); if (!claim) { @@ -121,36 +124,46 @@ function ClaimMenuList(props: Props) { // $FlowFixMe (contentClaim.value.stream_type === 'audio' || contentClaim.value.stream_type === 'video'); + function handleAdd(source, name, collectionId) { + doToast({ + message: source ? __('Item removed from %name%', { name }) : __('Item added to %name%', { name }), + }); + doCollectionEdit(collectionId, { + claims: [contentClaim], + remove: source, + type: 'playlist', + }); + } + function handleFollow() { - const { channelName } = parseURI(permanentUrl); const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe; subscriptionHandler({ channelName: '@' + channelName, - uri: permanentUrl, + uri: contentChannelUri, notificationsDisabled: true, }); } function handleToggleMute() { if (channelIsMuted) { - doChannelUnmute(channelUri); + doChannelUnmute(contentChannelUri); } else { - doChannelMute(channelUri); + doChannelMute(contentChannelUri); } } function handleToggleBlock() { if (channelIsBlocked) { - doCommentModUnBlock(channelUri); + doCommentModUnBlock(contentChannelUri); } else { - doCommentModBlock(channelUri); + doCommentModBlock(contentChannelUri); } } function handleEdit() { if (!isChannel) { - const signingChannelName = signingChannel && signingChannel.name; + const signingChannelName = contentSigningChannel && contentSigningChannel.name; const uriObject: { streamName: string, streamClaimId: string, channelName?: string } = { streamName: claim.name, @@ -170,10 +183,10 @@ function ClaimMenuList(props: Props) { } function handleDelete() { - if (!isRepost && !isChannel) { - openModal(MODALS.CONFIRM_FILE_REMOVE, { uri }); + if (!repostedClaim && !isChannel) { + openModal(MODALS.CONFIRM_FILE_REMOVE, { uri, doGoBack: false }); } else { - openModal(MODALS.CONFIRM_CLAIM_REVOKE, { claim, cb: !isRepost && (() => replace(`/$/${PAGES.CHANNELS}`)) }); + openModal(MODALS.CONFIRM_CLAIM_REVOKE, { claim, cb: isChannel && (() => replace(`/$/${PAGES.CHANNELS}`)) }); } } @@ -183,9 +196,9 @@ function ClaimMenuList(props: Props) { function handleToggleAdminBlock() { if (channelIsAdminBlocked) { - doCommentModUnBlockAsAdmin(channelUri, ''); + doCommentModUnBlockAsAdmin(contentChannelUri, ''); } else { - doCommentModBlockAsAdmin(channelUri, ''); + doCommentModBlockAsAdmin(contentChannelUri, ''); } } @@ -210,7 +223,7 @@ function ClaimMenuList(props: Props) { function handleReportContent() { // $FlowFixMe - push(`/$/${PAGES.REPORT_CONTENT}?claimId=${(repostedContent && repostedContent.claim_id) || claim.claim_id}`); + push(`/$/${PAGES.REPORT_CONTENT}?claimId=${contentClaim && contentClaim.claim_id}`); } return ( @@ -228,101 +241,79 @@ function ClaimMenuList(props: Props) { {(!IS_WEB || (IS_WEB && isAuthenticated)) && ( <> <> - {/* WATCH LATER */} - {isPlayable && !collectionId && ( - { - doToast({ - message: hasClaimInWatchLater - ? __('Item removed from Watch Later') - : __('Item added to Watch Later'), - }); - doCollectionEdit(COLLECTIONS_CONSTS.WATCH_LATER_ID, { - claims: [contentClaim], - remove: hasClaimInWatchLater, - type: 'playlist', - }); - }} - > -
- - {hasClaimInWatchLater ? __('In Watch Later') : __('Watch Later')} -
-
- )} - {/* CUSTOM LIST */} - {isPlayable && !collectionId && ( - { - doToast({ - message: hasClaimInCustom - ? __('Item removed from %lastCollectionName%', { lastCollectionName }) - : __('Item added to %lastCollectionName%', { lastCollectionName }), - }); - doCollectionEdit(COLLECTIONS_CONSTS.FAVORITES_ID, { - claims: [contentClaim], - remove: hasClaimInCustom, - type: 'playlist', - }); - }} - > -
- - {hasClaimInCustom - ? __('In %lastCollectionName%', { lastCollectionName }) - : __(`${lastCollectionName}`)} -
-
- )} {/* COLLECTION OPERATIONS */} - {collectionId && collectionName && isCollectionClaim && ( + {collectionId && isCollectionClaim ? ( <> - {Boolean(editedCollection) && ( - push(`/$/${PAGES.LIST}/${collectionId}?view=edit`)} - > -
- - {__('Publish')} -
-
- )} push(`/$/${PAGES.LIST}/${collectionId}`)}>
{__('View List')}
- openModal(MODALS.COLLECTION_DELETE, { collectionId })} - > -
- - {__('Delete List')} -
-
+ {isMyCollection && ( + <> + push(`/$/${PAGES.LIST}/${collectionId}?view=edit`)} + > +
+ + {editedCollection ? __('Publish') : __('Edit List')} +
+
+ openModal(MODALS.COLLECTION_DELETE, { collectionId })} + > +
+ + {__('Delete List')} +
+
+ + )} - )} - {/* CURRENTLY ONLY SUPPORT PLAYLISTS FOR PLAYABLE; LATER DIFFERENT TYPES */} - {isPlayable && ( - openModal(MODALS.COLLECTION_ADD, { uri, type: 'playlist' })} - > -
- - {__('Add to Lists')} -
-
+ ) : ( + isPlayable && ( + <> + {/* WATCH LATER */} + handleAdd(hasClaimInWatchLater, 'Watch Later', COLLECTIONS_CONSTS.WATCH_LATER_ID)} + > +
+ + {hasClaimInWatchLater ? __('In Watch Later') : __('Watch Later')} +
+
+ {/* CUSTOM LIST */} + handleAdd(hasClaimInCustom, lastCollectionName, lastCollectionId)} + > +
+ + {hasClaimInCustom ? __(`In ${lastCollectionName}`) : __(`${lastCollectionName}`)} +
+
+ {/* CURRENTLY ONLY SUPPORT PLAYLISTS FOR PLAYABLE; LATER DIFFERENT TYPES */} + openModal(MODALS.COLLECTION_ADD, { uri, type: 'playlist' })} + > +
+ + {__('Add to Lists')} +
+
+
+ + ) )} + {!isChannelPage && ( <> -
@@ -332,7 +323,7 @@ function ClaimMenuList(props: Props) { )} - {!incognitoClaim && !isRepost && !claimIsMine && !isChannelPage && ( + {!incognitoClaim && !claimIsMine && !isChannelPage && ( <>
@@ -343,11 +334,11 @@ function ClaimMenuList(props: Props) { )} + {!isMyCollection && ( <> - {(!claimIsMine || channelIsBlocked) && channelUri ? ( - !incognitoClaim && - !isRepost && ( + {(!claimIsMine || channelIsBlocked) && contentChannelUri ? ( + !incognitoClaim && ( <>
@@ -376,7 +367,7 @@ function ClaimMenuList(props: Props) { ) ) : ( <> - {!isChannelPage && !isRepost && ( + {!isChannelPage && !repostedClaim && (
@@ -384,7 +375,6 @@ function ClaimMenuList(props: Props) {
)} - {showDelete && (
@@ -412,7 +402,7 @@ function ClaimMenuList(props: Props) {
- + {__('Copy Link')}
diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx index 850fb2f49..b7db22e10 100644 --- a/ui/component/claimPreview/view.jsx +++ b/ui/component/claimPreview/view.jsx @@ -144,12 +144,14 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { collectionUris, disableNavigation, } = props; - const isRepost = claim && claim.repost_channel_url; + const isCollection = claim && claim.value_type === 'collection'; + const collectionClaimId = isCollection && claim && claim.claim_id; + const listId = collectionId || collectionClaimId; const WrapperElement = wrapperElement || 'li'; const shouldFetch = claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta) && !pending); const abandoned = !isResolvingUri && !claim; - const isMyCollection = collectionId && (isCollectionMine || collectionId.includes('-')); + const isMyCollection = listId && (isCollectionMine || listId.includes('-')); const shouldHideActions = hideActions || isMyCollection || type === 'small' || type === 'tooltip'; const canonicalUrl = claim && claim.canonical_url; const lastCollectionIndex = collectionUris ? collectionUris.length - 1 : 0; @@ -171,16 +173,14 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { claim.value.stream_type && // $FlowFixMe (claim.value.stream_type === 'audio' || claim.value.stream_type === 'video'); - const isCollection = claim && claim.value_type === 'collection'; const isChannelUri = isValid ? parseURI(uri).isChannel : false; const signingChannel = claim && claim.signing_channel; let navigateUrl = formatLbryUrlForWeb((claim && claim.canonical_url) || uri || '/'); - if (collectionId) { + if (listId) { const collectionParams = new URLSearchParams(); - collectionParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, collectionId); + collectionParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, listId); navigateUrl = navigateUrl + `?` + collectionParams.toString(); } - const channelUri = !isChannelUri ? signingChannel && signingChannel.permanent_url : claim && claim.permanent_url; const navLinkProps = { to: navigateUrl, onClick: (e) => e.stopPropagation(), @@ -365,7 +365,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { {!pending && ( <> {renderActions && claim && renderActions(claim)} - {Boolean(isMyCollection && collectionId) && ( + {Boolean(isMyCollection && listId) && ( <>
@@ -379,7 +379,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { e.stopPropagation(); if (editCollection) { // $FlowFixMe - editCollection(collectionId, { + editCollection(listId, { order: { from: collectionIndex, to: Number(collectionIndex) - 1 }, }); } @@ -395,7 +395,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { e.stopPropagation(); if (editCollection) { // $FlowFixMe - editCollection(collectionId, { + editCollection(listId, { order: { from: collectionIndex, to: Number(collectionIndex + 1) }, }); } @@ -410,7 +410,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { e.preventDefault(); e.stopPropagation(); // $FlowFixMe - if (editCollection) editCollection(collectionId, { claims: [claim], remove: true }); + if (editCollection) editCollection(listId, { claims: [claim], remove: true }); }} />
@@ -452,7 +452,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => {
{!hideMenu && ( - + )} diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index 9919da141..3fe374e90 100644 --- a/ui/component/claimPreviewTile/view.jsx +++ b/ui/component/claimPreviewTile/view.jsx @@ -236,7 +236,7 @@ function ClaimPreviewTile(props: Props) {
)} - +
diff --git a/ui/modal/modalRemoveFile/index.js b/ui/modal/modalRemoveFile/index.js index d9016db8b..9d8dbfb9c 100644 --- a/ui/modal/modalRemoveFile/index.js +++ b/ui/modal/modalRemoveFile/index.js @@ -2,15 +2,16 @@ import { connect } from 'react-redux'; import { doDeleteFileAndMaybeGoBack } from 'redux/actions/file'; import { makeSelectTitleForUri, - makeSelectClaimIsMine, + doResolveUri, makeSelectClaimForUri, makeSelectIsAbandoningClaimForUri, } from 'lbry-redux'; import { doHideModal } from 'redux/actions/app'; import ModalRemoveFile from './view'; +import { makeSelectSigningIsMine } from 'redux/selectors/content'; const select = (state, props) => ({ - claimIsMine: makeSelectClaimIsMine(props.uri)(state), + claimIsMine: makeSelectSigningIsMine(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state), isAbandoning: makeSelectIsAbandoningClaimForUri(props.uri)(state), @@ -18,8 +19,9 @@ const select = (state, props) => ({ const perform = dispatch => ({ closeModal: () => dispatch(doHideModal()), - deleteFile: (uri, deleteFromComputer, abandonClaim) => { - dispatch(doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim)); + doResolveUri: (uri) => dispatch(doResolveUri(uri)), + deleteFile: (uri, deleteFromComputer, abandonClaim, doGoBack) => { + dispatch(doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim, doGoBack)); }, }); diff --git a/ui/modal/modalRemoveFile/view.jsx b/ui/modal/modalRemoveFile/view.jsx index 38bdba168..badd31a66 100644 --- a/ui/modal/modalRemoveFile/view.jsx +++ b/ui/modal/modalRemoveFile/view.jsx @@ -12,8 +12,10 @@ type Props = { uri: string, claim: StreamClaim, claimIsMine: boolean, + doResolveUri: (string) => void, closeModal: () => void, - deleteFile: (string, boolean, boolean) => void, + deleteFile: (string, boolean, boolean, boolean) => void, + doGoBack: boolean, title: string, fileInfo?: { outpoint: ?string, @@ -22,10 +24,16 @@ type Props = { }; function ModalRemoveFile(props: Props) { - const { uri, claimIsMine, closeModal, deleteFile, title, claim, isAbandoning } = props; + const { uri, claimIsMine, doResolveUri, closeModal, deleteFile, doGoBack = true, title, claim, isAbandoning } = props; const [deleteChecked, setDeleteChecked] = usePersistedState('modal-remove-file:delete', true); const [abandonChecked, setAbandonChecked] = usePersistedState('modal-remove-file:abandon', true); + React.useEffect(() => { + if (uri) { + doResolveUri(uri); + } + }, [uri, doResolveUri]); + return ( }} + tokens={{ lbc: }} > Remove from blockchain (%lbc%) @@ -87,7 +95,7 @@ function ModalRemoveFile(props: Props) { button="primary" label={isAbandoning ? __('Removing...') : __('OK')} disabled={isAbandoning || !(deleteChecked || abandonChecked)} - onClick={() => deleteFile(uri, deleteChecked, claimIsMine ? abandonChecked : false)} + onClick={() => deleteFile(uri, deleteChecked, claimIsMine ? abandonChecked : false, doGoBack)} />
diff --git a/ui/page/channel/view.jsx b/ui/page/channel/view.jsx index 6dd52cf26..1bd03b613 100644 --- a/ui/page/channel/view.jsx +++ b/ui/page/channel/view.jsx @@ -218,7 +218,7 @@ function ChannelPage(props: Props) { {!(isBlocked || isMuted) && } {!(isBlocked || isMuted) && (!channelIsBlackListed || isSubscribed) && } {/* TODO: add channel collections */} - + {cover && } {cover && } diff --git a/ui/redux/actions/comments.js b/ui/redux/actions/comments.js index b3349ec26..053e8eab1 100644 --- a/ui/redux/actions/comments.js +++ b/ui/redux/actions/comments.js @@ -24,6 +24,8 @@ import { makeSelectNotificationForCommentId } from 'redux/selectors/notification import { selectActiveChannelClaim } from 'redux/selectors/app'; import { toHex } from 'util/hex'; import Comments from 'comments'; +import { selectPrefsReady } from 'redux/selectors/sync'; +import { doAlertWaitingForSync } from 'redux/actions/app'; const isDev = process.env.NODE_ENV !== 'production'; @@ -726,8 +728,22 @@ function doCommentModToggleBlock( ) { return async (dispatch: Dispatch, getState: GetState) => { const state = getState(); - + const ready = selectPrefsReady(state); let blockerChannelClaims = selectMyChannelClaims(state); + + if (!ready) { + return dispatch(doAlertWaitingForSync()); + } + + if (!blockerChannelClaims) { + return dispatch( + doToast({ + message: __('Create a channel to change this setting.'), + isError: false, + }) + ); + } + if (blockerIds.length === 0) { // Specific blockers not provided, so find one based on block-level. switch (blockLevel) { diff --git a/ui/redux/actions/file.js b/ui/redux/actions/file.js index 7282b334d..447fd4f20 100644 --- a/ui/redux/actions/file.js +++ b/ui/redux/actions/file.js @@ -53,7 +53,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim, cb) { }; } -export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim) { +export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim, doGoBack) { return (dispatch, getState) => { const state = getState(); const playingUri = selectPlayingUri(state); @@ -70,7 +70,9 @@ export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim doDeleteFile(outpoint || claimOutpoint, deleteFromComputer, abandonClaim, (abandonState) => { if (abandonState === ABANDON_STATES.DONE) { if (abandonClaim) { - dispatch(goBack()); + if (doGoBack) { + dispatch(goBack()); + } dispatch(doHideModal()); } }