Allow renaming collection in view playlist page.

This commit is contained in:
Franco Montenegro 2022-03-28 15:18:03 -03:00 committed by jessopb
parent 1369fbb064
commit 5a24d6c570
9 changed files with 77 additions and 102 deletions

View file

@ -29,6 +29,7 @@ type Props = {
doToggleShuffleList: (string, boolean) => void, doToggleShuffleList: (string, boolean) => void,
playNextUri: string, playNextUri: string,
firstItem: string, firstItem: string,
onRenameCollection: () => void,
}; };
function CollectionActions(props: Props) { function CollectionActions(props: Props) {
@ -48,6 +49,7 @@ function CollectionActions(props: Props) {
firstItem, firstItem,
showEdit, showEdit,
setShowEdit, setShowEdit,
onRenameCollection,
} = props; } = props;
const [doShuffle, setDoShuffle] = React.useState(false); const [doShuffle, setDoShuffle] = React.useState(false);
const { push } = useHistory(); const { push } = useHistory();
@ -120,6 +122,17 @@ function CollectionActions(props: Props) {
{!isBuiltin && {!isBuiltin &&
(isMyCollection ? ( (isMyCollection ? (
<> <>
{!uri && (
<Button
title={__('Rename')}
label={__('Rename')}
className={classnames('button--file-action')}
onClick={onRenameCollection}
icon={ICONS.EDIT}
iconSize={18}
disabled={claimIsPending}
/>
)}
<Button <Button
title={uri ? __('Update') : __('Publish')} title={uri ? __('Update') : __('Publish')}
label={uri ? __('Update') : __('Publish')} label={uri ? __('Update') : __('Publish')}

View file

@ -16,19 +16,10 @@ type Props = {
collectionId: string, collectionId: string,
playNextUri: string, playNextUri: string,
doToggleShuffleList: (string) => void, doToggleShuffleList: (string) => void,
onRenameList: () => void,
}; };
function CollectionMenuList(props: Props) { function CollectionMenuList(props: Props) {
const { const { inline = false, collectionId, collectionName, doOpenModal, playNextUri, doToggleShuffleList } = props;
inline = false,
collectionId,
collectionName,
doOpenModal,
playNextUri,
doToggleShuffleList,
onRenameList,
} = props;
const [doShuffle, setDoShuffle] = React.useState(false); const [doShuffle, setDoShuffle] = React.useState(false);
const { push } = useHistory(); const { push } = useHistory();
@ -95,12 +86,6 @@ function CollectionMenuList(props: Props) {
{__('Delete List')} {__('Delete List')}
</div> </div>
</MenuItem> </MenuItem>
<MenuItem onSelect={onRenameList}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.EDIT} />
{__('Rename List')}
</div>
</MenuItem>
</> </>
)} )}
</MenuList> </MenuList>

View file

@ -16,7 +16,7 @@ import {
makeSelectCountForCollectionId, makeSelectCountForCollectionId,
makeSelectIsResolvingCollectionForId, makeSelectIsResolvingCollectionForId,
} from 'redux/selectors/collections'; } from 'redux/selectors/collections';
import { doFetchItemsInCollection, doCollectionDelete, doCollectionRename } from 'redux/actions/collections'; import { doFetchItemsInCollection, doCollectionDelete } from 'redux/actions/collections';
import { doResolveUri } from 'redux/actions/claims'; import { doResolveUri } from 'redux/actions/claims';
import { selectMutedChannels } from 'redux/selectors/blocked'; import { selectMutedChannels } from 'redux/selectors/blocked';
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc'; import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
@ -54,7 +54,6 @@ const perform = (dispatch) => ({
resolveUri: (uri) => dispatch(doResolveUri(uri)), resolveUri: (uri) => dispatch(doResolveUri(uri)),
resolveCollectionItems: (options) => doFetchItemsInCollection(options), resolveCollectionItems: (options) => doFetchItemsInCollection(options),
deleteCollection: (id) => dispatch(doCollectionDelete(id)), deleteCollection: (id) => dispatch(doCollectionDelete(id)),
renameCollection: (id, name) => dispatch(doCollectionRename(id, name)),
}); });
export default connect(select, perform)(CollectionPreviewTile); export default connect(select, perform)(CollectionPreviewTile);

View file

@ -1,10 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import * as ICONS from 'constants/icons';
import { NavLink, useHistory } from 'react-router-dom'; import { NavLink, useHistory } from 'react-router-dom';
import { FormField } from 'component/common/form';
import Button from 'component/button';
import ClaimPreviewTile from 'component/claimPreviewTile'; import ClaimPreviewTile from 'component/claimPreviewTile';
import TruncatedText from 'component/common/truncated-text'; import TruncatedText from 'component/common/truncated-text';
import CollectionCount from './collectionCount'; import CollectionCount from './collectionCount';
@ -43,7 +40,6 @@ type Props = {
deleteCollection: (string) => void, deleteCollection: (string) => void,
resolveCollectionItems: (any) => void, resolveCollectionItems: (any) => void,
isResolvingCollectionClaims: boolean, isResolvingCollectionClaims: boolean,
renameCollection: (string, string) => void,
}; };
function CollectionPreviewTile(props: Props) { function CollectionPreviewTile(props: Props) {
@ -57,15 +53,10 @@ function CollectionPreviewTile(props: Props) {
collectionItemUrls, collectionItemUrls,
claim, claim,
resolveCollectionItems, resolveCollectionItems,
renameCollection,
} = props; } = props;
const { push } = useHistory(); const { push } = useHistory();
const hasClaim = Boolean(claim); const hasClaim = Boolean(claim);
const [isRenamingList, setIsRenamingList] = React.useState(false);
const [newName, setNewName] = React.useState(collectionName);
React.useEffect(() => { React.useEffect(() => {
if (collectionId && hasClaim && resolveCollectionItems) { if (collectionId && hasClaim && resolveCollectionItems) {
resolveCollectionItems({ collectionId, page_size: 5 }); resolveCollectionItems({ collectionId, page_size: 5 });
@ -78,16 +69,11 @@ function CollectionPreviewTile(props: Props) {
formatLbryUrlForWeb(collectionItemUrls[0] || '/') + (collectionId ? generateListSearchUrlParams(collectionId) : ''); formatLbryUrlForWeb(collectionItemUrls[0] || '/') + (collectionId ? generateListSearchUrlParams(collectionId) : '');
function handleClick(e) { function handleClick(e) {
if (navigateUrl && !isRenamingList) { if (navigateUrl) {
push(navigateUrl); push(navigateUrl);
} }
} }
function handleRenameCollection() {
renameCollection(collectionId, newName);
setIsRenamingList(false);
}
const navLinkProps = { const navLinkProps = {
to: navigateUrl, to: navigateUrl,
onClick: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(),
@ -158,44 +144,12 @@ function CollectionPreviewTile(props: Props) {
</React.Fragment> </React.Fragment>
</FileThumbnail> </FileThumbnail>
</NavLink> </NavLink>
{isRenamingList && ( <NavLink {...navLinkProps}>
<FormField <h2 className="claim-tile__title">
autoFocus <TruncatedText text={collectionName} lines={1} />
type="text" <CollectionMenuList collectionId={collectionId} />
name="rename_collection" </h2>
value={newName} </NavLink>
label={__('New Name')}
inputButton={
<>
<Button
button={'alt'}
icon={ICONS.COMPLETE}
className={'button-toggle'}
disabled={collectionName === newName}
onClick={handleRenameCollection}
/>
<Button
button={'alt'}
className={'button-toggle'}
icon={ICONS.REMOVE}
onClick={() => {
setIsRenamingList(false);
setNewName(collectionName);
}}
/>
</>
}
onChange={(e) => setNewName(e.target.value)}
/>
)}
{!isRenamingList && (
<NavLink {...navLinkProps}>
<h2 className="claim-tile__title">
<TruncatedText text={collectionName} lines={1} />
<CollectionMenuList collectionId={collectionId} onRenameList={() => setIsRenamingList(true)} />
</h2>
</NavLink>
)}
<div> <div>
<div className="claim-tile__info"> <div className="claim-tile__info">
<React.Fragment> <React.Fragment>

View file

@ -195,7 +195,6 @@ export const COLLECTION_ITEMS_RESOLVE_STARTED = 'COLLECTION_ITEMS_RESOLVE_STARTE
export const COLLECTION_ITEMS_RESOLVE_COMPLETED = 'COLLECTION_ITEMS_RESOLVE_COMPLETED'; export const COLLECTION_ITEMS_RESOLVE_COMPLETED = 'COLLECTION_ITEMS_RESOLVE_COMPLETED';
export const COLLECTION_ITEMS_RESOLVE_FAILED = 'COLLECTION_ITEMS_RESOLVE_FAILED'; export const COLLECTION_ITEMS_RESOLVE_FAILED = 'COLLECTION_ITEMS_RESOLVE_FAILED';
export const COLLECTION_NEW = 'COLLECTION_NEW'; export const COLLECTION_NEW = 'COLLECTION_NEW';
export const COLLECTION_RENAME = 'COLLECTION_RENAME';
export const COLLECTION_DELETE = 'COLLECTION_DELETE'; export const COLLECTION_DELETE = 'COLLECTION_DELETE';
export const COLLECTION_PENDING = 'COLLECTION_PENDING'; export const COLLECTION_PENDING = 'COLLECTION_PENDING';
export const COLLECTION_EDIT = 'COLLECTION_EDIT'; export const COLLECTION_EDIT = 'COLLECTION_EDIT';

View file

@ -54,6 +54,7 @@ const perform = (dispatch) => ({
fetchCollectionItems: (claimId, cb) => dispatch(doFetchItemsInCollection({ collectionId: claimId }, cb)), // if this collection is not resolved, resolve it fetchCollectionItems: (claimId, cb) => dispatch(doFetchItemsInCollection({ collectionId: claimId }, cb)), // if this collection is not resolved, resolve it
deleteCollection: (id, colKey) => dispatch(doCollectionDelete(id, colKey)), deleteCollection: (id, colKey) => dispatch(doCollectionDelete(id, colKey)),
editCollection: (id, params) => dispatch(doCollectionEdit(id, params)), editCollection: (id, params) => dispatch(doCollectionEdit(id, params)),
renameCollection: (id, name) => dispatch(doCollectionEdit(id, { name })),
}); });
export default withRouter(connect(select, perform)(CollectionPage)); export default withRouter(connect(select, perform)(CollectionPage));

View file

@ -19,6 +19,7 @@ import * as COLLECTIONS_CONSTS from 'constants/collections';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import Spinner from 'component/spinner'; import Spinner from 'component/spinner';
import { FormField } from 'component/common/form';
export const PAGE_VIEW_QUERY = 'view'; export const PAGE_VIEW_QUERY = 'view';
export const EDIT_PAGE = 'edit'; export const EDIT_PAGE = 'edit';
@ -42,6 +43,7 @@ type Props = {
fetchCollectionItems: (string, () => void) => void, fetchCollectionItems: (string, () => void) => void,
resolveUris: (string) => void, resolveUris: (string) => void,
user: ?User, user: ?User,
renameCollection: (string, string) => void,
}; };
export default function CollectionPage(props: Props) { export default function CollectionPage(props: Props) {
@ -58,6 +60,7 @@ export default function CollectionPage(props: Props) {
editCollection, editCollection,
fetchCollectionItems, fetchCollectionItems,
deleteCollection, deleteCollection,
renameCollection,
} = props; } = props;
const { const {
@ -70,6 +73,11 @@ export default function CollectionPage(props: Props) {
const [showEdit, setShowEdit] = React.useState(false); const [showEdit, setShowEdit] = React.useState(false);
const [unavailableUris, setUnavailable] = React.useState([]); const [unavailableUris, setUnavailable] = React.useState([]);
const collectionName = claim ? claim.value.title || claim.name : collection && collection.name;
const [isRenamingList, setIsRenamingList] = React.useState(false);
const [newName, setNewName] = React.useState(collectionName);
const { name, totalItems } = collection || {}; const { name, totalItems } = collection || {};
const isBuiltin = COLLECTIONS_CONSTS.BUILTIN_LISTS.includes(collectionId); const isBuiltin = COLLECTIONS_CONSTS.BUILTIN_LISTS.includes(collectionId);
@ -84,6 +92,11 @@ export default function CollectionPage(props: Props) {
editCollection(collectionId, { order: { from, to } }); editCollection(collectionId, { order: { from, to } });
} }
function handleRenameCollection() {
renameCollection(collectionId, newName);
setIsRenamingList(false);
}
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const editing = urlParams.get(PAGE_VIEW_QUERY) === EDIT_PAGE; const editing = urlParams.get(PAGE_VIEW_QUERY) === EDIT_PAGE;
@ -142,17 +155,48 @@ export default function CollectionPage(props: Props) {
const info = ( const info = (
<Card <Card
title={ title={
<span> isRenamingList ? (
<Icon <FormField
icon={ autoFocus
(collectionId === COLLECTIONS_CONSTS.WATCH_LATER_ID && ICONS.TIME) || type="text"
(collectionId === COLLECTIONS_CONSTS.FAVORITES_ID && ICONS.STAR) || name="rename_collection"
ICONS.STACK value={newName}
label={__('New Collection Name')}
inputButton={
<>
<Button
button={'alt'}
icon={ICONS.COMPLETE}
className={'button-toggle'}
disabled={!(newName || '').trim() || collectionName === newName}
onClick={handleRenameCollection}
/>
<Button
button={'alt'}
className={'button-toggle'}
icon={ICONS.REMOVE}
onClick={() => {
setIsRenamingList(false);
setNewName(collectionName);
}}
/>
</>
} }
className="icon--margin-right" onChange={(e) => setNewName(e.target.value)}
/> />
{claim ? claim.value.title || claim.name : collection && collection.name} ) : (
</span> <span>
<Icon
icon={
(collectionId === COLLECTIONS_CONSTS.WATCH_LATER_ID && ICONS.TIME) ||
(collectionId === COLLECTIONS_CONSTS.FAVORITES_ID && ICONS.STAR) ||
ICONS.STACK
}
className="icon--margin-right"
/>
{collectionName}
</span>
)
} }
titleActions={unavailableUris.length > 0 ? removeUnavailable : titleActions} titleActions={unavailableUris.length > 0 ? removeUnavailable : titleActions}
subtitle={subTitle} subtitle={subTitle}
@ -166,6 +210,7 @@ export default function CollectionPage(props: Props) {
collectionUrls={collectionUrls} collectionUrls={collectionUrls}
setShowEdit={setShowEdit} setShowEdit={setShowEdit}
showEdit={showEdit} showEdit={showEdit}
onRenameCollection={() => setIsRenamingList(true)}
/> />
} }
actions={ actions={

View file

@ -40,14 +40,6 @@ export const doLocalCollectionCreate = (
}); });
}; };
export const doCollectionRename = (id: string, newName: string) => ({
type: ACTIONS.COLLECTION_RENAME,
data: {
id,
newName,
},
});
export const doCollectionDelete = (id: string, colKey: ?string = undefined) => ( export const doCollectionDelete = (id: string, colKey: ?string = undefined) => (
dispatch: Dispatch, dispatch: Dispatch,
getState: GetState getState: GetState

View file

@ -58,19 +58,6 @@ const collectionsReducer = handleActions(
}; };
}, },
[ACTIONS.COLLECTION_RENAME]: (state, action) => {
const { unpublished } = state;
const { id, newName } = action.data;
const newUnpublished = { ...unpublished };
if (newUnpublished[id]) {
newUnpublished[id].name = newName;
}
return {
...state,
unpublished: newUnpublished,
};
},
[ACTIONS.COLLECTION_DELETE]: (state, action) => { [ACTIONS.COLLECTION_DELETE]: (state, action) => {
const { lastUsedCollection } = state; const { lastUsedCollection } = state;
const { id, collectionKey } = action.data; const { id, collectionKey } = action.data;