Remove individual entries
This commit is contained in:
parent
1a7b92c4fa
commit
ea686474fc
6 changed files with 92 additions and 166 deletions
|
@ -60,6 +60,7 @@ type Props = {
|
||||||
droppableProvided?: any,
|
droppableProvided?: any,
|
||||||
unavailableUris?: Array<string>,
|
unavailableUris?: Array<string>,
|
||||||
showMemberBadge?: boolean,
|
showMemberBadge?: boolean,
|
||||||
|
inHistory?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ClaimList(props: Props) {
|
export default function ClaimList(props: Props) {
|
||||||
|
@ -100,6 +101,7 @@ export default function ClaimList(props: Props) {
|
||||||
droppableProvided,
|
droppableProvided,
|
||||||
unavailableUris,
|
unavailableUris,
|
||||||
showMemberBadge,
|
showMemberBadge,
|
||||||
|
inHistory,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
|
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
|
||||||
|
@ -202,6 +204,7 @@ export default function ClaimList(props: Props) {
|
||||||
dragHandleProps={draggableProvided && draggableProvided.dragHandleProps}
|
dragHandleProps={draggableProvided && draggableProvided.dragHandleProps}
|
||||||
unavailableUris={unavailableUris}
|
unavailableUris={unavailableUris}
|
||||||
showMemberBadge={showMemberBadge}
|
showMemberBadge={showMemberBadge}
|
||||||
|
inHistory={inHistory}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import { ENABLE_NO_SOURCE_CLAIMS } from 'config';
|
||||||
import CollectionEditButtons from 'component/collectionEditButtons';
|
import CollectionEditButtons from 'component/collectionEditButtons';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import { useIsMobile } from 'effects/use-screensize';
|
import { useIsMobile } from 'effects/use-screensize';
|
||||||
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
|
|
||||||
const AbandonedChannelPreview = lazyImport(() =>
|
const AbandonedChannelPreview = lazyImport(() =>
|
||||||
import('component/abandonedChannelPreview' /* webpackChunkName: "abandonedChannelPreview" */)
|
import('component/abandonedChannelPreview' /* webpackChunkName: "abandonedChannelPreview" */)
|
||||||
|
@ -96,6 +97,7 @@ type Props = {
|
||||||
dragHandleProps?: any,
|
dragHandleProps?: any,
|
||||||
unavailableUris?: Array<string>,
|
unavailableUris?: Array<string>,
|
||||||
showMemberBadge?: boolean,
|
showMemberBadge?: boolean,
|
||||||
|
inHistory?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
|
@ -161,6 +163,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
dragHandleProps,
|
dragHandleProps,
|
||||||
unavailableUris,
|
unavailableUris,
|
||||||
showMemberBadge,
|
showMemberBadge,
|
||||||
|
inHistory,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
@ -211,6 +214,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
? claim.permanent_url || claim.canonical_url
|
? claim.permanent_url || claim.canonical_url
|
||||||
: undefined;
|
: undefined;
|
||||||
const repostedContentUri = claim && (claim.reposted_claim ? claim.reposted_claim.permanent_url : claim.permanent_url);
|
const repostedContentUri = claim && (claim.reposted_claim ? claim.reposted_claim.permanent_url : claim.permanent_url);
|
||||||
|
const [watchHistory, setHistory] = usePersistedState('watch-history', []);
|
||||||
|
|
||||||
// Get channel title ( use name as fallback )
|
// Get channel title ( use name as fallback )
|
||||||
let channelTitle = null;
|
let channelTitle = null;
|
||||||
|
@ -281,6 +285,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
|
|
||||||
function handleOnClick(e) {
|
function handleOnClick(e) {
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
|
console.log('click: ', e);
|
||||||
onClick(e, claim, indexInContainer);
|
onClick(e, claim, indexInContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +297,14 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeFromHistory(e, uri) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (watchHistory.find((entry) => entry === uri)) {
|
||||||
|
watchHistory.splice(watchHistory.indexOf(uri), 1);
|
||||||
|
setHistory(watchHistory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isValid && !isResolvingUri && shouldFetch && uri) {
|
if (isValid && !isResolvingUri && shouldFetch && uri) {
|
||||||
resolveUri(uri);
|
resolveUri(uri);
|
||||||
|
@ -364,7 +377,6 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />}
|
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={classnames('claim-preview', {
|
className={classnames('claim-preview', {
|
||||||
'claim-preview--small': type === 'small' || type === 'tooltip',
|
'claim-preview--small': type === 'small' || type === 'tooltip',
|
||||||
|
@ -487,7 +499,11 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{inHistory && (
|
||||||
|
<div onClick={(e) => removeFromHistory(e, uri)} className="claim-preview__history-remove">
|
||||||
|
<Icon icon={ICONS.REMOVE} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{/* Todo: check isLivestreamActive once we have that data consistently everywhere. */}
|
{/* Todo: check isLivestreamActive once we have that data consistently everywhere. */}
|
||||||
{claim && isLivestream && <ClaimPreviewReset uri={uri} />}
|
{claim && isLivestream && <ClaimPreviewReset uri={uri} />}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ const PLAYLISTS: SideNavLink = {
|
||||||
const HISTORY: SideNavLink = {
|
const HISTORY: SideNavLink = {
|
||||||
title: 'History',
|
title: 'History',
|
||||||
link: `/$/${PAGES.HISTORY}`,
|
link: `/$/${PAGES.HISTORY}`,
|
||||||
icon: ICONS.TIME,
|
icon: ICONS.EYE,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,7 @@ function VideoViewer(props: Props) {
|
||||||
|
|
||||||
const [history, setHistory] = usePersistedState('watch-history', []);
|
const [history, setHistory] = usePersistedState('watch-history', []);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!history[0].indexOf(claim.permanent_url) !== -1) {
|
if (!history[0] || !history[0].indexOf(claim.permanent_url) !== -1) {
|
||||||
if (!history || !history.length) {
|
if (!history || !history.length) {
|
||||||
setHistory([claim.permanent_url]);
|
setHistory([claim.permanent_url]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -306,7 +306,6 @@ function VideoViewer(props: Props) {
|
||||||
setIsEndedEmbed(false);
|
setIsEndedEmbed(false);
|
||||||
setReplay(false);
|
setReplay(false);
|
||||||
setDoNavigate(false);
|
setDoNavigate(false);
|
||||||
// setWatchHistory()
|
|
||||||
analytics.videoIsPlaying(true, player);
|
analytics.videoIsPlaying(true, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,27 +2,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ClaimList from 'component/claimList';
|
import ClaimList from 'component/claimList';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
import Card from 'component/common/card';
|
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
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 usePersistedState from 'effects/use-persisted-state';
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const Lazy = {
|
|
||||||
// $FlowFixMe
|
|
||||||
DragDropContext: React.lazy(() => import('react-beautiful-dnd' /* webpackChunkName: "dnd" */).then((module) => ({ default: module.DragDropContext }))),
|
|
||||||
// $FlowFixMe
|
|
||||||
Droppable: React.lazy(() => import('react-beautiful-dnd' /* webpackChunkName: "dnd" */).then((module) => ({ default: module.Droppable }))),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PAGE_VIEW_QUERY = 'view';
|
export const PAGE_VIEW_QUERY = 'view';
|
||||||
export const EDIT_PAGE = 'edit';
|
// export const EDIT_PAGE = 'edit';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
collectionId: string,
|
collectionId: string,
|
||||||
|
@ -31,163 +19,51 @@ type Props = {
|
||||||
thumbnail: string,
|
thumbnail: string,
|
||||||
collectionUrls: Array<string>,
|
collectionUrls: Array<string>,
|
||||||
isResolvingCollection: boolean,
|
isResolvingCollection: boolean,
|
||||||
isMyClaim: boolean,
|
// isMyClaim: boolean,
|
||||||
isMyCollection: boolean,
|
// isMyCollection: boolean,
|
||||||
claimIsPending: boolean,
|
// claimIsPending: boolean,
|
||||||
collectionHasEdits: boolean,
|
// collectionHasEdits: boolean,
|
||||||
deleteCollection: (string, string) => void,
|
// deleteCollection: (string, string) => void,
|
||||||
editCollection: (string, CollectionEditParams) => void,
|
// editCollection: (string, CollectionEditParams) => void,
|
||||||
fetchCollectionItems: (string, () => void) => void,
|
fetchCollectionItems: (string, () => void) => void,
|
||||||
resolveUris: (string) => void,
|
resolveUris: (string) => void,
|
||||||
user: ?User,
|
user: ?User,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function HistoryPage(props: Props) {
|
export default function HistoryPage(props: Props) {
|
||||||
const {
|
const { collectionId } = props;
|
||||||
collectionId,
|
const [history, setHistory] = usePersistedState('watch-history', []);
|
||||||
claim,
|
const [unavailableUris] = React.useState([]);
|
||||||
collectionHasEdits,
|
|
||||||
claimIsPending,
|
|
||||||
isResolvingCollection,
|
|
||||||
editCollection,
|
|
||||||
fetchCollectionItems,
|
|
||||||
deleteCollection,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const collection = {
|
function clearHistory() {
|
||||||
id: 'watchhistory',
|
setHistory([]);
|
||||||
name: __('Watch History'),
|
|
||||||
type: 'playlist',
|
|
||||||
};
|
|
||||||
const collectionUrls = [];
|
|
||||||
|
|
||||||
const [history] = usePersistedState('watch-history', []);
|
|
||||||
|
|
||||||
const {
|
|
||||||
location: { search },
|
|
||||||
} = useHistory();
|
|
||||||
|
|
||||||
const [didTryResolve, setDidTryResolve] = React.useState(false);
|
|
||||||
const [unavailableUris, setUnavailable] = React.useState([]);
|
|
||||||
const isBuiltin = COLLECTIONS_CONSTS.BUILTIN_LISTS.includes(collectionId);
|
|
||||||
|
|
||||||
function handleOnDragEnd(result) {
|
|
||||||
const { source, destination } = result;
|
|
||||||
|
|
||||||
if (!destination) return;
|
|
||||||
|
|
||||||
const { index: from } = source;
|
|
||||||
const { index: to } = destination;
|
|
||||||
|
|
||||||
editCollection(collectionId, { order: { from, to } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(search);
|
return (
|
||||||
const editing = urlParams.get(PAGE_VIEW_QUERY) === EDIT_PAGE;
|
<Page className="historyPage-wrapper">
|
||||||
|
<div className={classnames('section card-stack')}>
|
||||||
|
<div className="claim-list__header">
|
||||||
|
<h1 className="card__title">
|
||||||
|
<Icon icon={ICONS.EYE} style={{ marginRight: 'var(--spacing-s)' }} />
|
||||||
|
{__('Watch History')}
|
||||||
|
</h1>
|
||||||
|
|
||||||
const urlsReady = collectionUrls && history.length;
|
<div className="claim-list__alt-controls--wrap">
|
||||||
|
{history.length > 0 && (
|
||||||
React.useEffect(() => {
|
<Button
|
||||||
if (collectionId && !urlsReady && !didTryResolve && !collection) {
|
title={__('Clear History')}
|
||||||
fetchCollectionItems(collectionId, () => setDidTryResolve(true));
|
button="primary"
|
||||||
}
|
label={__('Clear History')}
|
||||||
}, [collectionId, urlsReady, didTryResolve, setDidTryResolve, fetchCollectionItems, collection]);
|
onClick={() => clearHistory()}
|
||||||
|
/>
|
||||||
const pending = (
|
)}
|
||||||
<div className="help card__title--help">
|
</div>
|
||||||
<Spinner type={'small'} />
|
|
||||||
{__('Your publish is being confirmed and will be live soon')}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const unpublished = (
|
|
||||||
<Button
|
|
||||||
button="close"
|
|
||||||
icon={ICONS.REFRESH}
|
|
||||||
label={__('Clear Edits')}
|
|
||||||
onClick={() => deleteCollection(collectionId, COLLECTIONS_CONSTS.COL_KEY_EDITED)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const removeUnavailable = (
|
|
||||||
<Button
|
|
||||||
button="close"
|
|
||||||
icon={ICONS.DELETE}
|
|
||||||
label={__('Remove all unavailable claims')}
|
|
||||||
onClick={() => {
|
|
||||||
editCollection(collectionId, { uris: unavailableUris, remove: true });
|
|
||||||
setUnavailable([]);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
let titleActions;
|
|
||||||
if (collectionHasEdits) {
|
|
||||||
titleActions = unpublished;
|
|
||||||
} else if (claimIsPending) {
|
|
||||||
titleActions = pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
const listName = claim ? claim.value.title || claim.name : collection && collection.name;
|
|
||||||
|
|
||||||
const info = (
|
|
||||||
<Card
|
|
||||||
title={
|
|
||||||
<span>
|
|
||||||
<Icon
|
|
||||||
icon={
|
|
||||||
(collectionId === COLLECTIONS_CONSTS.WATCH_LATER_ID && ICONS.TIME) ||
|
|
||||||
(collectionId === COLLECTIONS_CONSTS.FAVORITES_ID && ICONS.STAR) ||
|
|
||||||
ICONS.TIME
|
|
||||||
}
|
|
||||||
className="icon--margin-right"
|
|
||||||
/>
|
|
||||||
{isBuiltin ? __(listName) : listName}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
titleActions={unavailableUris.length > 0 ? removeUnavailable : titleActions}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!collection && (isResolvingCollection || !didTryResolve)) {
|
|
||||||
return (
|
|
||||||
<Page>
|
|
||||||
<h2 className="main--empty empty">{__('Loading...')}</h2>
|
|
||||||
</Page>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!collection && !isResolvingCollection && didTryResolve) {
|
|
||||||
return (
|
|
||||||
<Page>
|
|
||||||
<h2 className="main--empty empty">{__('Nothing here')}</h2>
|
|
||||||
</Page>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (urlsReady) {
|
|
||||||
return (
|
|
||||||
<Page className="playlistPage-wrapper">
|
|
||||||
{editing}
|
|
||||||
<div className={classnames('section card-stack')}>
|
|
||||||
{info}
|
|
||||||
<React.Suspense fallback={null}>
|
|
||||||
<Lazy.DragDropContext onDragEnd={handleOnDragEnd}>
|
|
||||||
<Lazy.Droppable droppableId="list__ordering">
|
|
||||||
{(DroppableProvided) => (
|
|
||||||
<ClaimList
|
|
||||||
uris={history}
|
|
||||||
collectionId={collectionId}
|
|
||||||
// showEdit={showEdit}
|
|
||||||
droppableProvided={DroppableProvided}
|
|
||||||
unavailableUris={unavailableUris}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Lazy.Droppable>
|
|
||||||
</Lazy.DragDropContext>
|
|
||||||
</React.Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
</Page>
|
{history.length > 0 && (
|
||||||
);
|
<ClaimList uris={history} collectionId={collectionId} unavailableUris={unavailableUris} inHistory />
|
||||||
}
|
)}
|
||||||
|
{history.length === 0 && <h2 className="main--empty empty">{__('Nothing here')}</h2>}
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1205,6 +1205,38 @@ img {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.historyPage-wrapper {
|
||||||
|
.claim-preview__wrapper {
|
||||||
|
.claim-preview__history-remove {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--spacing-s);
|
||||||
|
right: var(--spacing-xs);
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.icon {
|
||||||
|
stroke: var(--color-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.menu__button.claim__menu-button {
|
||||||
|
top: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.claim-preview__history-remove {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.premium-wrapper {
|
.premium-wrapper {
|
||||||
.membership_title {
|
.membership_title {
|
||||||
.comment__badge {
|
.comment__badge {
|
||||||
|
|
Loading…
Reference in a new issue