From 5fdac4898ff7ea01671425508e61134bbc212cbc Mon Sep 17 00:00:00 2001 From: jessopb <36554050+jessopb@users.noreply.github.com> Date: Thu, 27 Jan 2022 10:20:21 -0500 Subject: [PATCH] Playlistorder (#7442) * Add horizontal layout (#636) * Test out a horizontal scroll for upcoming (tile only for now) * - add support for list layout - add following label on home page - clan up css and naming conventions * Update header type + show only if scheduled streams are showing * [Playlist] Pull in sorting changes from desktop + Add Drag-n-Drop + Handle unavailable/deleted claims (#641) * Add ordering Icons * Refactor doCollectionEdit - It required claims as parameter, when only uris are used to populate the collection, so that was changed to pass down the uris instead. - There were unused and mostly unnecessary functions inside, for example the parameter claimIds was never used so it would never enter the claimSearch function which again would be used to generate uris, so it's better to just use uris as parameter * Add List Reordering changes * Add toggle button for list editing * Add toggle on content page collection sidebar * Enable drag-n-drop to re-order list items https://www.youtube.com/watch?v=aYZRRyukuIw * Allow removing all unavailable claims from a List * Fix on icons * Fix section buttons positioning * Move preventDefault and stopPropagation to buttons div instead of each button, preventing clicking even if disabled opening the claim * Change dragging cursor * Fix sizing * Fix dragging component * Restrict dragging to vertical axis * Ignore shuffle state for ordering * Fix console errors * Mobile fixes * Fix sidebar spacing * Fix grey on mobile after click * cleanup Co-authored-by: Dan Peterson Co-authored-by: saltrafael <76502841+saltrafael@users.noreply.github.com> --- .flowconfig | 3 +- flow-typed/Collections.js | 3 +- package.json | 3 +- static/app-strings.json | 6 + ui/component/claimCollectionAdd/view.jsx | 26 +- ui/component/claimList/view.jsx | 153 +++++-- ui/component/claimListDiscover/view.jsx | 38 +- ui/component/claimMenuList/view.jsx | 8 +- ui/component/claimPreview/index.js | 13 +- ui/component/claimPreview/view.jsx | 69 +-- ui/component/claimPreviewTile/view.jsx | 6 +- ui/component/collectionActions/view.jsx | 64 ++- .../collectionContentSidebar/index.js | 7 +- .../collectionContentSidebar/view.jsx | 77 +++- ui/component/collectionEdit/index.js | 4 +- ui/component/collectionEdit/view.jsx | 36 +- ui/component/collectionEditButtons/index.js | 23 + ui/component/collectionEditButtons/view.jsx | 92 ++++ ui/component/collectionSelectItem/index.js | 8 +- ui/component/collectionSelectItem/view.jsx | 6 +- ui/component/common/icon-custom.jsx | 19 +- ui/component/fileWatchLaterLink/index.js | 7 +- ui/component/fileWatchLaterLink/view.jsx | 9 +- ui/component/router/view.jsx | 5 +- ui/constants/icons.js | 3 +- ui/page/collection/view.jsx | 47 ++- ui/page/home/view.jsx | 39 +- ui/redux/actions/collections.js | 160 ++----- ui/redux/selectors/collections.js | 4 +- ui/scss/all.scss | 2 + ui/scss/component/_button.scss | 39 +- ui/scss/component/_claim-list.scss | 19 +- ui/scss/component/_collection.scss | 6 +- ui/scss/component/_main.scss | 103 +++-- ui/scss/component/_swipe-list.scss | 13 + ui/scss/component/_utils.scss | 92 ++++ ui/util/buildHomepage.js | 4 +- ui/util/claim.js | 23 + ui/util/string.js | 24 +- yarn.lock | 395 ++++++++++++------ 40 files changed, 1114 insertions(+), 544 deletions(-) create mode 100644 ui/component/collectionEditButtons/index.js create mode 100644 ui/component/collectionEditButtons/view.jsx create mode 100644 ui/scss/component/_swipe-list.scss create mode 100644 ui/scss/component/_utils.scss diff --git a/.flowconfig b/.flowconfig index 8a6fdca75..2b4def62b 100644 --- a/.flowconfig +++ b/.flowconfig @@ -2,7 +2,8 @@ .*\.typeface\.json .*/node_modules/findup/.* .*/node_modules/react-plastic/.* - +.*/node_modules/raf-schd/.* +.*/node_modules/react-beautiful-dnd/.* [include] diff --git a/flow-typed/Collections.js b/flow-typed/Collections.js index f70825a4f..a6939307f 100644 --- a/flow-typed/Collections.js +++ b/flow-typed/Collections.js @@ -24,9 +24,8 @@ declare type CollectionGroup = { } declare type CollectionEditParams = { - claims?: Array, + uris?: Array, remove?: boolean, - claimIds?: Array, replace?: boolean, order?: { from: number, to: number }, type?: string, diff --git a/package.json b/package.json index 9a83474d8..8aed5063e 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,8 @@ "parse-duration": "^1.0.0", "proxy-polyfill": "0.1.6", "re-reselect": "^4.0.0", - "react-datetime-picker": "^3.2.1", + "react-beautiful-dnd": "^13.1.0", + "react-datetime-picker": "^3.4.3", "remove-markdown": "^0.3.0", "rss": "^1.2.2", "source-map-explorer": "^2.5.2", diff --git a/static/app-strings.json b/static/app-strings.json index 0e1d72689..3d730e596 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2249,5 +2249,11 @@ "Amount of $%input_amount% LBC in USB is lower than price of $%price_amount%": "Amount of $%input_amount% LBC in USB is lower than price of $%price_amount%", "Hosting for content you have downloaded": "Hosting for content you have downloaded", "Hosting content selected by the network": "Hosting content selected by the network", + "Remove all unavailable claims": "Remove all unavailable claims", + "Drag": "Drag", + "Move Top": "Move Top", + "Move Bottom": "Move Bottom", + "Move Up": "Move Up", + "Move Down": "Move Down", "--end--": "--end--" } diff --git a/ui/component/claimCollectionAdd/view.jsx b/ui/component/claimCollectionAdd/view.jsx index 848445866..445ed25c4 100644 --- a/ui/component/claimCollectionAdd/view.jsx +++ b/ui/component/claimCollectionAdd/view.jsx @@ -79,15 +79,7 @@ const ClaimCollectionAdd = (props: Props) => { .filter((list) => (isChannel ? list.type === 'collection' : list.type === 'playlist')) .map((l) => { const { id } = l; - return ( - - ); + return ; })} {unpublished && (Object.values(unpublished): any) @@ -96,13 +88,7 @@ const ClaimCollectionAdd = (props: Props) => { .map((l) => { const { id } = l; return ( - + ); })} {published && @@ -110,13 +96,7 @@ const ClaimCollectionAdd = (props: Props) => { // $FlowFixMe const { id } = l; return ( - + ); })} diff --git a/ui/component/claimList/view.jsx b/ui/component/claimList/view.jsx index aeac02b0a..891f926b8 100644 --- a/ui/component/claimList/view.jsx +++ b/ui/component/claimList/view.jsx @@ -1,4 +1,7 @@ // @flow + +// $FlowFixMe +import { Draggable } from 'react-beautiful-dnd'; import { MAIN_CLASS } from 'constants/classnames'; import type { Node } from 'react'; import React, { useEffect } from 'react'; @@ -44,7 +47,13 @@ type Props = { collectionId?: string, showNoSourceClaims?: boolean, onClick?: (e: any, claim?: ?Claim, index?: number) => void, - noEmpty: boolean, + maxClaimRender?: number, + excludeUris?: Array, + loadedCallback?: (number) => void, + swipeLayout: boolean, + showEdit?: boolean, + droppableProvided?: any, + unavailableUris?: Array, }; export default function ClaimList(props: Props) { @@ -75,7 +84,13 @@ export default function ClaimList(props: Props) { collectionId, showNoSourceClaims, onClick, - noEmpty, + maxClaimRender, + excludeUris = [], + loadedCallback, + swipeLayout = false, + showEdit, + droppableProvided, + unavailableUris, } = props; const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW); @@ -85,8 +100,18 @@ export default function ClaimList(props: Props) { const timedOut = uris === null; const urisLength = (uris && uris.length) || 0; - const tileUris = (prefixUris || []).concat(uris); - const sortedUris = (urisLength > 0 && (currentSort === SORT_NEW ? tileUris : tileUris.slice().reverse())) || []; + let tileUris = (prefixUris || []).concat(uris || []); + tileUris = tileUris.filter((uri) => !excludeUris.includes(uri)); + + const totalLength = tileUris.length; + + if (maxClaimRender) tileUris = tileUris.slice(0, maxClaimRender); + + let sortedUris = (urisLength > 0 && (currentSort === SORT_NEW ? tileUris : tileUris.slice().reverse())) || []; + + React.useEffect(() => { + if (typeof loadedCallback === 'function') loadedCallback(totalLength); + }, [totalLength]); // eslint-disable-line react-hooks/exhaustive-deps const noResultMsg = searchInLanguage ? __('No results. Contents may be hidden by the Language filter.') @@ -96,11 +121,21 @@ export default function ClaimList(props: Props) { setCurrentSort(currentSort === SORT_NEW ? SORT_OLD : SORT_NEW); } - function handleClaimClicked(e, claim, index) { - if (onClick) { - onClick(e, claim, index); - } - } + const handleClaimClicked = React.useCallback( + (e, claim, index) => { + if (onClick) { + onClick(e, claim, index); + } + }, + [onClick] + ); + + const customShouldHide = React.useCallback((claim: StreamClaim) => { + // Hack to hide spee.ch thumbnail publishes + // If it meets these requirements, it was probably uploaded here: + // https://github.com/lbryio/lbry-redux/blob/master/src/redux/actions/publish.js#L74-L79 + return claim.name.length === 24 && !claim.name.includes(' ') && claim.value.author === 'Spee.ch'; + }, []); useEffect(() => { const handleScroll = debounce((e) => { @@ -123,8 +158,32 @@ export default function ClaimList(props: Props) { } }, [loading, onScrollBottom, urisLength, pageSize, page]); + const getClaimPreview = (uri: string, index: number, draggableProvided?: any) => ( + + ); + return tileLayout && !header ? ( -
+
{urisLength > 0 && tileUris.map((uri) => ( ))} - {!timedOut && urisLength === 0 && !loading && !noEmpty && ( -
{empty || noResultMsg}
- )} + {!timedOut && urisLength === 0 && !loading &&
{empty || noResultMsg}
} {timedOut && timedOutMessage &&
{timedOutMessage}
}
) : ( @@ -178,43 +236,52 @@ export default function ClaimList(props: Props) { {urisLength > 0 && (
    - {sortedUris.map((uri, index) => ( - - {injectedItem && index === 4 &&
  • {injectedItem}
  • } - { - // Hack to hide spee.ch thumbnail publishes - // If it meets these requirements, it was probably uploaded here: - // https://github.com/lbryio/lbry-redux/blob/master/src/redux/actions/publish.js#L74-L79 - return claim.name.length === 24 && !claim.name.includes(' ') && claim.value.author === 'Spee.ch'; + {injectedItem && sortedUris.some((uri, index) => index === 4) &&
  • {injectedItem}
  • } + + {sortedUris.map((uri, index) => + droppableProvided ? ( + + {(draggableProvided, draggableSnapshot) => { + // Restrict dragging to vertical axis + // https://github.com/atlassian/react-beautiful-dnd/issues/958#issuecomment-980548919 + let transform = draggableProvided.draggableProps.style.transform; + + if (draggableSnapshot.isDragging && transform) { + transform = transform.replace(/\(.+,/, '(0,'); + } + + const style = { + ...draggableProvided.draggableProps.style, + transform, + }; + + return ( +
  • + {/* https://github.com/atlassian/react-beautiful-dnd/issues/1756 */} +
    + + {getClaimPreview(uri, index, draggableProvided)} +
  • + ); }} - onClick={(e, claim, index) => handleClaimClicked(e, claim, index)} - /> -
    - ))} + + ) : ( + getClaimPreview(uri, index) + ) + )} + + {droppableProvided && droppableProvided.placeholder}
)} - {!timedOut && urisLength === 0 && !loading && !noEmpty && ( -
{empty || noResultMsg}
- )} + {!timedOut && urisLength === 0 && !loading &&
{empty || noResultMsg}
} {!loading && timedOut && timedOutMessage &&
{timedOutMessage}
}
); diff --git a/ui/component/claimListDiscover/view.jsx b/ui/component/claimListDiscover/view.jsx index 7f3e03065..c457b55a5 100644 --- a/ui/component/claimListDiscover/view.jsx +++ b/ui/component/claimListDiscover/view.jsx @@ -94,6 +94,13 @@ type Props = { doClaimSearch: ({}) => void, doToggleTagFollowDesktop: (string) => void, doFetchViewCount: (claimIdCsv: string) => void, + + loadedCallback?: (number) => void, + maxClaimRender?: number, + useSkeletonScreen?: boolean, + excludeUris?: Array, + + swipeLayout: boolean, }; function ClaimListDiscover(props: Props) { @@ -157,6 +164,11 @@ function ClaimListDiscover(props: Props) { empty, claimsByUri, doFetchViewCount, + loadedCallback, + maxClaimRender, + useSkeletonScreen = true, + excludeUris = [], + swipeLayout = false, } = props; const didNavigateForward = history.action === 'PUSH'; const { search } = location; @@ -493,6 +505,21 @@ function ClaimListDiscover(props: Props) { } function resolveOrderByOption(orderBy: string | Array, sortBy: string | Array) { + // let order_by; // peterson 038692cafc793616cceaf10b88909fecde07ad0b + // + // switch (orderBy) { + // case CS.ORDER_BY_TRENDING: + // order_by = CS.ORDER_BY_TRENDING_VALUE; + // break; + // case CS.ORDER_BY_NEW: + // order_by = CS.ORDER_BY_NEW_VALUE; + // break; + // case CS.ORDER_BY_NEW_ASC: + // order_by = CS.ORDER_BY_NEW_ASC_VALUE; + // break; + // default: + // order_by = CS.ORDER_BY_TOP_VALUE; + // } const order_by = orderBy === CS.ORDER_BY_TRENDING ? CS.ORDER_BY_TRENDING_VALUE @@ -569,8 +596,12 @@ function ClaimListDiscover(props: Props) { searchOptions={options} showNoSourceClaims={showNoSourceClaims} empty={empty} + maxClaimRender={maxClaimRender} + excludeUris={excludeUris} + loadedCallback={loadedCallback} + swipeLayout={swipeLayout} /> - {loading && ( + {loading && useSkeletonScreen && (
{new Array(dynamicPageSize).fill(1).map((x, i) => ( @@ -602,8 +633,13 @@ function ClaimListDiscover(props: Props) { searchOptions={options} showNoSourceClaims={hasNoSource || showNoSourceClaims} empty={empty} + maxClaimRender={maxClaimRender} + excludeUris={excludeUris} + loadedCallback={loadedCallback} + swipeLayout={swipeLayout} /> {loading && + useSkeletonScreen && new Array(dynamicPageSize) .fill(1) .map((x, i) => ( diff --git a/ui/component/claimMenuList/view.jsx b/ui/component/claimMenuList/view.jsx index f4b458841..c502875f6 100644 --- a/ui/component/claimMenuList/view.jsx +++ b/ui/component/claimMenuList/view.jsx @@ -157,11 +157,9 @@ function ClaimMenuList(props: Props) { doToast({ message: source ? __('Item removed from %name%', { name }) : __('Item added to %name%', { name }), }); - doCollectionEdit(collectionId, { - claims: [contentClaim], - remove: source, - type: 'playlist', - }); + if (contentClaim) { + doCollectionEdit(collectionId, { uris: [contentClaim.permanent_url], remove: source, type: 'playlist' }); + } } function handleFollow() { diff --git a/ui/component/claimPreview/index.js b/ui/component/claimPreview/index.js index 4e07f98c3..df20d0ac5 100644 --- a/ui/component/claimPreview/index.js +++ b/ui/component/claimPreview/index.js @@ -10,17 +10,12 @@ import { selectDateForUri, } from 'redux/selectors/claims'; import { makeSelectStreamingUrlForUri } from 'redux/selectors/file_info'; -import { - makeSelectCollectionIsMine, - makeSelectUrlsForCollectionId, - makeSelectIndexForUrlInCollection, -} from 'redux/selectors/collections'; +import { makeSelectCollectionIsMine } from 'redux/selectors/collections'; import { doResolveUri } from 'redux/actions/claims'; -import { doCollectionEdit } from 'redux/actions/collections'; import { doFileGet } from 'redux/actions/file'; import { selectBanStateForUri } from 'lbryinc'; -import { selectShowMatureContent } from 'redux/selectors/settings'; +import { selectLanguage, selectShowMatureContent } from 'redux/selectors/settings'; import { makeSelectHasVisitedUri } from 'redux/selectors/content'; import { selectIsSubscribedForUri } from 'redux/selectors/subscriptions'; import { isClaimNsfw } from 'util/claim'; @@ -50,15 +45,13 @@ const select = (state, props) => { streamingUrl: props.uri && makeSelectStreamingUrlForUri(props.uri)(state), wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state), isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state), - collectionUris: makeSelectUrlsForCollectionId(props.collectionId)(state), - collectionIndex: makeSelectIndexForUrlInCollection(props.uri, props.collectionId)(state), + lang: selectLanguage(state), }; }; const perform = (dispatch) => ({ resolveUri: (uri) => dispatch(doResolveUri(uri)), getFile: (uri) => dispatch(doFileGet(uri, false)), - editCollection: (id, params) => dispatch(doCollectionEdit(id, params)), }); export default connect(select, perform)(ClaimPreview); diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx index 25e0277a4..08fa04e67 100644 --- a/ui/component/claimPreview/view.jsx +++ b/ui/component/claimPreview/view.jsx @@ -6,10 +6,14 @@ import { isEmpty } from 'util/object'; import classnames from 'classnames'; import { isURIValid } from 'util/lbryURI'; import * as COLLECTIONS_CONSTS from 'constants/collections'; +import { isChannelClaim } from 'util/claim'; import { formatLbryUrlForWeb } from 'util/url'; import { formatClaimPreviewTitle } from 'util/formatAriaLabel'; +import { toCompactNotation } from 'util/string'; +import Tooltip from 'component/common/tooltip'; import FileThumbnail from 'component/fileThumbnail'; import UriIndicator from 'component/uriIndicator'; +import PreviewOverlayProperties from 'component/previewOverlayProperties'; import ClaimTags from 'component/claimTags'; import SubscribeButton from 'component/subscribeButton'; import ChannelThumbnail from 'component/channelThumbnail'; @@ -25,10 +29,8 @@ import ClaimMenuList from 'component/claimMenuList'; import ClaimPreviewLoading from './claim-preview-loading'; import ClaimPreviewHidden from './claim-preview-no-mature'; import ClaimPreviewNoContent from './claim-preview-no-content'; -import CollectionEditButtons from './collection-buttons'; - +import CollectionEditButtons from 'component/collectionEditButtons'; import AbandonedChannelPreview from 'component/abandonedChannelPreview'; -import PreviewOverlayProperties from 'component/previewOverlayProperties'; // preview images used on the landing page and on the channel page type Props = { @@ -49,7 +51,7 @@ type Props = { type: string, banState: { blacklisted?: boolean, filtered?: boolean, muted?: boolean, blocked?: boolean }, hasVisitedUri: boolean, - channelIsBlocked: boolean, + blockedUris: Array, actions: boolean | Node | string | number, properties: boolean | Node | string | number | ((Claim) => Node), empty?: Node, @@ -68,15 +70,17 @@ type Props = { repostUrl?: string, hideMenu?: boolean, collectionId?: string, - editCollection: (string, CollectionEditParams) => void, isCollectionMine: boolean, - collectionUris: Array, - collectionIndex?: number, disableNavigation?: boolean, mediaDuration?: string, date?: any, indexInContainer?: number, // The index order of this component within 'containerId'. channelSubCount?: number, + swipeLayout: boolean, + lang: string, + showEdit?: boolean, + dragHandleProps?: any, + unavailableUris?: Array, }; const ClaimPreview = forwardRef((props: Props, ref: any) => { @@ -97,7 +101,6 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { streamingUrl, mediaDuration, // user properties - channelIsBlocked, hasVisitedUri, // component history, @@ -129,34 +132,40 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { hideMenu = false, // repostUrl, collectionId, - collectionIndex, - editCollection, isCollectionMine, - collectionUris, disableNavigation, indexInContainer, channelSubCount, + swipeLayout = false, + lang, + showEdit, + dragHandleProps, + unavailableUris, } = props; + const isCollection = claim && claim.value_type === 'collection'; const collectionClaimId = isCollection && claim && claim.claim_id; - const listId = collectionId || collectionClaimId || null; + 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 = listId && (isCollectionMine || listId.includes('-')); + if (isMyCollection && claim === null && unavailableUris) unavailableUris.push(uri); + const shouldHideActions = hideActions || isMyCollection || type === 'small' || type === 'tooltip'; const canonicalUrl = claim && claim.canonical_url; - const lastCollectionIndex = collectionUris ? collectionUris.length - 1 : 0; const channelSubscribers = React.useMemo(() => { if (channelSubCount === undefined) { return ; } - const formattedSubCount = Number(channelSubCount).toLocaleString(); + const formattedSubCount = toCompactNotation(channelSubCount, lang, 10000); return ( - - {channelSubCount === 1 ? __('1 Follower') : __('%formattedSubCount% Followers', { formattedSubCount })} - + + + {channelSubCount === 1 ? __('1 Follower') : __('%formattedSubCount% Followers', { formattedSubCount })} + + ); }, [channelSubCount]); const isValid = uri && isURIValid(uri); @@ -170,12 +179,13 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { claim.value.stream_type && // $FlowFixMe (claim.value.stream_type === 'audio' || claim.value.stream_type === 'video'); - const isChannelUri = claim ? claim.value_type === 'channel' : false; + const isChannelUri = isChannelClaim(claim, uri); const signingChannel = claim && claim.signing_channel; const repostedChannelUri = claim && claim.repost_channel_url && claim.value_type === 'channel' ? claim.permanent_url || claim.canonical_url : undefined; + const repostedContentUri = claim && (claim.reposted_claim ? claim.reposted_claim.permanent_url : claim.permanent_url); // Get channel title ( use name as fallback ) let channelTitle = null; @@ -225,7 +235,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { ((abandoned && !showUnresolvedClaim) || (!claimIsMine && obscureNsfw && nsfw)); // This will be replaced once blocking is done at the wallet server level - if (claim && !claimIsMine && (banState.blacklisted || banState.filtered)) { + if (!shouldHide && !claimIsMine && (banState.blacklisted || banState.filtered)) { shouldHide = true; } @@ -320,18 +330,14 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { 'claim-preview--channel': isChannelUri, 'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri, 'claim-preview--pending': pending, - 'claim-preview--collection-mine': isMyCollection && listId && type === 'listview', + 'claim-preview--collection-mine': isMyCollection && showEdit, + 'swipe-list__item': swipeLayout, })} > - {isMyCollection && listId && type === 'listview' && ( - + {isMyCollection && showEdit && ( + )} + {isChannelUri && claim ? ( @@ -342,7 +348,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => {
- {isPlayable && } + {isPlayable && }
{/* @if TARGET='app' */}
@@ -352,7 +358,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => {
{/* @endif */}
- +
@@ -391,8 +397,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => {
)} - - {isChannelUri && !channelIsBlocked && !claimIsMine && ( + {isChannelUri && !banState.muted && !claimIsMine && ( diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index 908ec5f1b..d8150f995 100644 --- a/ui/component/claimPreviewTile/view.jsx +++ b/ui/component/claimPreviewTile/view.jsx @@ -42,6 +42,7 @@ type Props = { properties?: (Claim) => void, collectionId?: string, viewCount: string, + swipeLayout: boolean, }; // preview image cards used in related video functionality, channel overview page and homepage @@ -66,6 +67,7 @@ function ClaimPreviewTile(props: Props) { collectionId, mediaDuration, viewCount, + swipeLayout = false, } = props; const isRepost = claim && claim.repost_channel_url; const isCollection = claim && claim.value_type === 'collection'; @@ -84,6 +86,7 @@ function ClaimPreviewTile(props: Props) { const thumbnailUrl = useGetThumbnail(uri, claim, streamingUrl, getFile, placeholder) || thumbnail; const canonicalUrl = claim && claim.canonical_url; const permanentUrl = claim && claim.permanent_url; + const repostedContentUri = claim && (claim.reposted_claim ? claim.reposted_claim.permanent_url : claim.permanent_url); const listId = collectionId || collectionClaimId; const navigateUrl = formatLbryUrlForWeb(canonicalUrl || uri || '/') + (listId ? generateListSearchUrlParams(listId) : ''); @@ -168,6 +171,7 @@ function ClaimPreviewTile(props: Props) { onClick={handleClick} className={classnames('card claim-preview--tile', { 'claim-preview__wrapper--channel': isChannel, + 'swipe-list__item claim-preview--horizontal-tile': swipeLayout, })} > @@ -175,7 +179,7 @@ function ClaimPreviewTile(props: Props) { {!isChannel && (
- {isPlayable && } + {isPlayable && }
{/* @if TARGET='app' */}
diff --git a/ui/component/collectionActions/view.jsx b/ui/component/collectionActions/view.jsx index 0a06d9bb7..1322a7002 100644 --- a/ui/component/collectionActions/view.jsx +++ b/ui/component/collectionActions/view.jsx @@ -55,20 +55,6 @@ function CollectionActions(props: Props) { const claimId = claim && claim.claim_id; const webShareable = true; // collections have cost? - /* - A bit too much dependency with both ordering and shuffling depending on a single list item index selector - For now when they click edit, we'll toggle shuffle off for them. - */ - const handleSetShowEdit = (setting) => { - doToggleShuffleList(collectionId, false); - setShowEdit(setting); - }; - - const handlePublishMode = () => { - doToggleShuffleList(collectionId, false); - push(`?${PAGE_VIEW_QUERY}=${EDIT_PAGE}`); - }; - const doPlay = React.useCallback( (playUri) => { const navigateUrl = formatLbryUrlForWeb(playUri); @@ -138,7 +124,7 @@ function CollectionActions(props: Props) { title={uri ? __('Update') : __('Publish')} label={uri ? __('Update') : __('Publish')} className={classnames('button--file-action')} - onClick={() => handlePublishMode()} + onClick={() => push(`?${PAGE_VIEW_QUERY}=${EDIT_PAGE}`)} icon={ICONS.PUBLISH} iconColor={collectionHasEdits && 'red'} iconSize={18} @@ -165,26 +151,28 @@ function CollectionActions(props: Props) { ); - const infoButton = ( -
); if (isMobile) { @@ -192,7 +180,7 @@ function CollectionActions(props: Props) {
{lhsSection} {rhsSection} - {uri && {infoButton}} + {infoButtons}
); } else { @@ -202,10 +190,8 @@ function CollectionActions(props: Props) { {lhsSection} {rhsSection} -
- {uri && infoButton} - {showEditButton} -
+ + {infoButtons} ); } diff --git a/ui/component/collectionContentSidebar/index.js b/ui/component/collectionContentSidebar/index.js index e65f79f84..876f47240 100644 --- a/ui/component/collectionContentSidebar/index.js +++ b/ui/component/collectionContentSidebar/index.js @@ -1,13 +1,15 @@ import { connect } from 'react-redux'; import CollectionContent from './view'; -import { selectClaimForUri, selectClaimIsMine } from 'redux/selectors/claims'; +import { selectClaimForUri } from 'redux/selectors/claims'; import { makeSelectUrlsForCollectionId, makeSelectNameForCollectionId, makeSelectCollectionForId, + makeSelectCollectionIsMine, } from 'redux/selectors/collections'; import { selectPlayingUri, selectListLoop, selectListShuffle } from 'redux/selectors/content'; import { doToggleLoopList, doToggleShuffleList } from 'redux/actions/content'; +import { doCollectionEdit } from 'redux/actions/collections'; const select = (state, props) => { const playingUri = selectPlayingUri(state); @@ -24,7 +26,7 @@ const select = (state, props) => { collection: makeSelectCollectionForId(props.id)(state), collectionUrls: makeSelectUrlsForCollectionId(props.id)(state), collectionName: makeSelectNameForCollectionId(props.id)(state), - isMine: selectClaimIsMine(state, claim), + isMyCollection: makeSelectCollectionIsMine(props.id)(state), loop, shuffle, }; @@ -33,4 +35,5 @@ const select = (state, props) => { export default connect(select, { doToggleLoopList, doToggleShuffleList, + doCollectionEdit, })(CollectionContent); diff --git a/ui/component/collectionContentSidebar/view.jsx b/ui/component/collectionContentSidebar/view.jsx index e21302803..5ba082225 100644 --- a/ui/component/collectionContentSidebar/view.jsx +++ b/ui/component/collectionContentSidebar/view.jsx @@ -1,5 +1,10 @@ // @flow + +// $FlowFixMe +import { DragDropContext, Droppable } from 'react-beautiful-dnd'; + import React from 'react'; +import classnames from 'classnames'; import ClaimList from 'component/claimList'; import Card from 'component/common/card'; import Button from 'component/button'; @@ -11,7 +16,7 @@ import * as ICONS from 'constants/icons'; type Props = { id: string, url: string, - isMine: boolean, + isMyCollection: boolean, collectionUrls: Array, collectionName: string, collection: any, @@ -20,10 +25,35 @@ type Props = { doToggleLoopList: (string, boolean) => void, doToggleShuffleList: (string, string, boolean) => void, createUnpublishedCollection: (string, Array, ?string) => void, + doCollectionEdit: (string, CollectionEditParams) => void, }; export default function CollectionContent(props: Props) { - const { collectionUrls, collectionName, id, url, loop, shuffle, doToggleLoopList, doToggleShuffleList } = props; + const { + isMyCollection, + collectionUrls, + collectionName, + id, + url, + loop, + shuffle, + doToggleLoopList, + doToggleShuffleList, + doCollectionEdit, + } = props; + + const [showEdit, setShowEdit] = React.useState(false); + + function handleOnDragEnd(result) { + const { source, destination } = result; + + if (!destination) return; + + const { index: from } = source; + const { index: to } = destination; + + doCollectionEdit(id, { order: { from, to } }); + } return ( } titleActions={ -
- {/* TODO: BUTTON TO SAVE COLLECTION - Probably save/copy modal */} -
+ <> +
+ {/* TODO: BUTTON TO SAVE COLLECTION - Probably save/copy modal */} +
+ + {isMyCollection && ( + + + ); + }; + function getRowElements(title, route, link, icon, help, options, index, pinUrls) { const tilePlaceholder = (
    {new Array(options.pageSize || 8).fill(1).map((x, i) => ( - + ))}
); - const claimTiles = ( - - ); + const claimTiles = ; return (
+ {/* category header */} {index !== 0 && title && typeof title === 'string' && ( -

- -

+ )} {index === 0 && <>{claimTiles}} @@ -69,6 +83,7 @@ function HomePage(props: Props) { )} + {/* view more button */} {(route || link) && (