Allow renaming collection in view playlist page.
This commit is contained in:
parent
1369fbb064
commit
5a24d6c570
9 changed files with 77 additions and 102 deletions
|
@ -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')}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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={
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue