[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 <g> 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
This commit is contained in:
saltrafael 2022-01-12 16:14:12 -03:00 committed by zeppi
parent a26305d75a
commit 1fad908711
32 changed files with 868 additions and 499 deletions

View file

@ -2,7 +2,8 @@
.*\.typeface\.json
.*/node_modules/findup/.*
.*/node_modules/react-plastic/.*
.*/node_modules/raf-schd/.*
.*/node_modules/react-beautiful-dnd/.*
[include]

View file

@ -24,9 +24,8 @@ declare type CollectionGroup = {
}
declare type CollectionEditParams = {
claims?: Array<Claim>,
uris?: Array<string>,
remove?: boolean,
claimIds?: Array<string>,
replace?: boolean,
order?: { from: number, to: number },
type?: string,

View file

@ -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",

View file

@ -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--"
}

View file

@ -79,15 +79,7 @@ const ClaimCollectionAdd = (props: Props) => {
.filter((list) => (isChannel ? list.type === 'collection' : list.type === 'playlist'))
.map((l) => {
const { id } = l;
return (
<CollectionSelectItem
claim={claim}
collectionId={id}
uri={permanentUrl}
key={id}
category={'builtin'}
/>
);
return <CollectionSelectItem collectionId={id} uri={permanentUrl} key={id} category={'builtin'} />;
})}
{unpublished &&
(Object.values(unpublished): any)
@ -96,13 +88,7 @@ const ClaimCollectionAdd = (props: Props) => {
.map((l) => {
const { id } = l;
return (
<CollectionSelectItem
claim={claim}
collectionId={id}
uri={permanentUrl}
key={id}
category={'unpublished'}
/>
<CollectionSelectItem collectionId={id} uri={permanentUrl} key={id} category={'unpublished'} />
);
})}
{published &&
@ -110,13 +96,7 @@ const ClaimCollectionAdd = (props: Props) => {
// $FlowFixMe
const { id } = l;
return (
<CollectionSelectItem
claim={claim}
collectionId={id}
uri={permanentUrl}
key={id}
category={'published'}
/>
<CollectionSelectItem collectionId={id} uri={permanentUrl} key={id} category={'published'} />
);
})}
</div>

View file

@ -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';
@ -48,6 +51,9 @@ type Props = {
excludeUris?: Array<string>,
loadedCallback?: (number) => void,
swipeLayout: boolean,
showEdit?: boolean,
droppableProvided?: any,
unavailableUris?: Array<string>,
};
export default function ClaimList(props: Props) {
@ -82,6 +88,9 @@ export default function ClaimList(props: Props) {
excludeUris = [],
loadedCallback,
swipeLayout = false,
showEdit,
droppableProvided,
unavailableUris,
} = props;
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
@ -149,6 +158,30 @@ export default function ClaimList(props: Props) {
}
}, [loading, onScrollBottom, urisLength, pageSize, page]);
const getClaimPreview = (uri: string, index: number, draggableProvided?: any) => (
<ClaimPreview
uri={uri}
indexInContainer={index}
type={type}
active={activeUri && uri === activeUri}
hideMenu={hideMenu}
includeSupportAction={includeSupportAction}
showUnresolvedClaim={showUnresolvedClaims}
properties={renderProperties || (type !== 'small' ? undefined : false)}
renderActions={renderActions}
showUserBlocked={showHiddenByUser}
showHiddenByUser={showHiddenByUser}
collectionId={collectionId}
showNoSourceClaims={showNoSourceClaims}
customShouldHide={customShouldHide}
onClick={handleClaimClicked}
swipeLayout={swipeLayout}
showEdit={showEdit}
dragHandleProps={draggableProvided && draggableProvided.dragHandleProps}
unavailableUris={unavailableUris}
/>
);
return tileLayout && !header ? (
<section className={classnames('claim-grid', { 'swipe-list': swipeLayout })}>
{urisLength > 0 &&
@ -207,30 +240,44 @@ export default function ClaimList(props: Props) {
'claim-list--card-body': tileLayout,
'swipe-list': swipeLayout,
})}
{...(droppableProvided && droppableProvided.droppableProps)}
ref={droppableProvided && droppableProvided.innerRef}
>
{sortedUris.map((uri, index) => (
<React.Fragment key={uri}>
{injectedItem && index === 4 && <li>{injectedItem}</li>}
<ClaimPreview
uri={uri}
indexInContainer={index}
type={type}
active={activeUri && uri === activeUri}
hideMenu={hideMenu}
includeSupportAction={includeSupportAction}
showUnresolvedClaim={showUnresolvedClaims}
properties={renderProperties || (type !== 'small' ? undefined : false)}
renderActions={renderActions}
showUserBlocked={showHiddenByUser}
showHiddenByUser={showHiddenByUser}
collectionId={collectionId}
showNoSourceClaims={showNoSourceClaims}
customShouldHide={customShouldHide}
onClick={handleClaimClicked}
swipeLayout={swipeLayout}
/>
</React.Fragment>
))}
{injectedItem && sortedUris.some((uri, index) => index === 4) && <li>{injectedItem}</li>}
{sortedUris.map((uri, index) =>
droppableProvided ? (
<Draggable key={uri} draggableId={uri} index={index}>
{(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 (
<li ref={draggableProvided.innerRef} {...draggableProvided.draggableProps} style={style}>
{/* https://github.com/atlassian/react-beautiful-dnd/issues/1756 */}
<div style={{ display: 'none' }} {...draggableProvided.dragHandleProps} />
{getClaimPreview(uri, index, draggableProvided)}
</li>
);
}}
</Draggable>
) : (
getClaimPreview(uri, index)
)
)}
{droppableProvided && droppableProvided.placeholder}
</ul>
)}

View file

@ -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() {

View file

@ -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);

View file

@ -9,8 +9,11 @@ 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';
@ -26,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 = {
@ -69,16 +70,17 @@ type Props = {
repostUrl?: string,
hideMenu?: boolean,
collectionId?: string,
editCollection: (string, CollectionEditParams) => void,
isCollectionMine: boolean,
collectionUris: Array<Collection>,
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<string>,
};
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
@ -130,15 +132,17 @@ const ClaimPreview = forwardRef<any, {}>((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;
@ -147,18 +151,21 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
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 <span />;
}
const formattedSubCount = Number(channelSubCount).toLocaleString();
const formattedSubCount = toCompactNotation(channelSubCount, lang, 10000);
return (
<span className="claim-preview__channel-sub-count">
{channelSubCount === 1 ? __('1 Follower') : __('%formattedSubCount% Followers', { formattedSubCount })}
</span>
<Tooltip title={channelSubCount} followCursor placement="top">
<span className="claim-preview__channel-sub-count">
{channelSubCount === 1 ? __('1 Follower') : __('%formattedSubCount% Followers', { formattedSubCount })}
</span>
</Tooltip>
);
}, [channelSubCount]);
const isValid = uri && isURIValid(uri);
@ -178,6 +185,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
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;
@ -227,7 +235,7 @@ const ClaimPreview = forwardRef<any, {}>((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;
}
@ -322,19 +330,14 @@ const ClaimPreview = forwardRef<any, {}>((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' && (
<CollectionEditButtons
collectionIndex={collectionIndex}
editCollection={editCollection}
listId={listId}
lastCollectionIndex={lastCollectionIndex}
claim={claim}
/>
{isMyCollection && showEdit && (
<CollectionEditButtons uri={uri} collectionId={listId} dragHandleProps={dragHandleProps} />
)}
{isChannelUri && claim ? (
<UriIndicator focusable={false} uri={uri} link>
<ChannelThumbnail uri={uri} small={type === 'inline'} />
@ -345,7 +348,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
<NavLink aria-hidden tabIndex={-1} {...navLinkProps}>
<FileThumbnail thumbnail={thumbnailUrl}>
<div className="claim-preview__hover-actions">
{isPlayable && <FileWatchLaterLink focusable={false} uri={uri} />}
{isPlayable && <FileWatchLaterLink focusable={false} uri={repostedContentUri} />}
</div>
{/* @if TARGET='app' */}
<div className="claim-preview__hover-actions">
@ -355,7 +358,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
</div>
{/* @endif */}
<div className="claim-preview__file-property-overlay">
<PreviewOverlayProperties uri={uri} properties={properties} />
<PreviewOverlayProperties uri={uri} />
</div>
</FileThumbnail>
</NavLink>

View file

@ -86,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) : '');
@ -178,7 +179,7 @@ function ClaimPreviewTile(props: Props) {
{!isChannel && (
<React.Fragment>
<div className="claim-preview__hover-actions">
{isPlayable && <FileWatchLaterLink focusable={false} uri={uri} />}
{isPlayable && <FileWatchLaterLink focusable={false} uri={repostedContentUri} />}
</div>
{/* @if TARGET='app' */}
<div className="claim-preview__hover-actions">

View file

@ -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 = (
<Button
title={__('Info')}
className={classnames('button-toggle', {
'button-toggle--active': showInfo,
})}
icon={ICONS.MORE}
onClick={() => setShowInfo(!showInfo)}
/>
);
const infoButtons = (
<div className="section">
{uri && (
<Button
title={__('Info')}
className={classnames('button-toggle', {
'button-toggle--active': showInfo,
})}
icon={ICONS.MORE}
onClick={() => setShowInfo(!showInfo)}
/>
)}
const showEditButton = (
<Button
title={__('Edit')}
className={classnames('button-toggle', {
'button-toggle--active': showEdit,
})}
icon={ICONS.EDIT}
onClick={() => handleSetShowEdit(!showEdit)}
/>
{isMyCollection && (
<Button
title={__('Edit')}
className={classnames('button-toggle', { 'button-toggle--active': showEdit })}
icon={ICONS.EDIT}
onClick={() => setShowEdit(!showEdit)}
/>
)}
</div>
);
if (isMobile) {
@ -192,7 +180,7 @@ function CollectionActions(props: Props) {
<div className="media__actions">
{lhsSection}
{rhsSection}
{uri && <span>{infoButton}</span>}
{infoButtons}
</div>
);
} else {
@ -202,10 +190,8 @@ function CollectionActions(props: Props) {
{lhsSection}
{rhsSection}
</div>
<div className="section">
{uri && infoButton}
{showEditButton}
</div>
{infoButtons}
</div>
);
}

View file

@ -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);

View file

@ -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<Claim>,
collectionName: string,
collection: any,
@ -20,10 +25,35 @@ type Props = {
doToggleLoopList: (string, boolean) => void,
doToggleShuffleList: (string, string, boolean) => void,
createUnpublishedCollection: (string, Array<any>, ?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 (
<Card
@ -63,20 +93,39 @@ export default function CollectionContent(props: Props) {
</>
}
titleActions={
<div className="card__title-actions--link">
{/* TODO: BUTTON TO SAVE COLLECTION - Probably save/copy modal */}
<Button label={__('View List')} button="link" navigate={`/$/${PAGES.LIST}/${id}`} />
</div>
<>
<div className="card__title-actions--link">
{/* TODO: BUTTON TO SAVE COLLECTION - Probably save/copy modal */}
<Button label={__('View List')} button="link" navigate={`/$/${PAGES.LIST}/${id}`} />
</div>
{isMyCollection && (
<Button
title={__('Edit')}
className={classnames('button-toggle', { 'button-toggle--active': showEdit })}
icon={ICONS.EDIT}
onClick={() => setShowEdit(!showEdit)}
/>
)}
</>
}
body={
<ClaimList
isCardBody
type="small"
activeUri={url}
uris={collectionUrls}
collectionId={id}
empty={__('List is Empty')}
/>
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId="list__ordering">
{(DroppableProvided) => (
<ClaimList
isCardBody
type="small"
activeUri={url}
uris={collectionUrls}
collectionId={id}
empty={__('List is Empty')}
showEdit={showEdit}
droppableProvided={DroppableProvided}
/>
)}
</Droppable>
</DragDropContext>
}
/>
);

View file

@ -22,6 +22,7 @@ import * as ACTIONS from 'constants/action_types';
import CollectionForm from './view';
import { selectActiveChannelClaim, selectIncognito } from 'redux/selectors/app';
import { doSetActiveChannel, doSetIncognito } from 'redux/actions/app';
import { doCollectionEdit } from 'redux/actions/collections';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
@ -44,12 +45,13 @@ const select = (state, props) => ({
collectionClaimIds: makeSelectClaimIdsForCollectionId(props.collectionId)(state),
});
const perform = (dispatch) => ({
const perform = (dispatch, ownProps) => ({
publishCollectionUpdate: (params) => dispatch(doCollectionPublishUpdate(params)),
publishCollection: (params, collectionId) => dispatch(doCollectionPublish(params, collectionId)),
clearCollectionErrors: () => dispatch({ type: ACTIONS.CLEAR_COLLECTION_ERRORS }),
setActiveChannel: (claimId) => dispatch(doSetActiveChannel(claimId)),
setIncognito: (incognito) => dispatch(doSetIncognito(incognito)),
doCollectionEdit: (params) => dispatch(doCollectionEdit(ownProps.collectionId, params)),
});
export default connect(select, perform)(CollectionForm);

View file

@ -1,4 +1,7 @@
// @flow
// $FlowFixMe
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import React from 'react';
import classnames from 'classnames';
import Button from 'component/button';
@ -54,6 +57,7 @@ type Props = {
onDone: (string) => void,
setActiveChannel: (string) => void,
setIncognito: (boolean) => void,
doCollectionEdit: (CollectionEditParams) => void,
};
function CollectionForm(props: Props) {
@ -87,6 +91,7 @@ function CollectionForm(props: Props) {
setActiveChannel,
setIncognito,
onDone,
doCollectionEdit,
} = props;
const activeChannelName = activeChannelClaim && activeChannelClaim.name;
let prefix = 'lbry://';
@ -196,6 +201,17 @@ function CollectionForm(props: Props) {
return collectionParams;
}
function handleOnDragEnd(result) {
const { source, destination } = result;
if (!destination) return;
const { index: from } = source;
const { index: to } = destination;
doCollectionEdit({ order: { from, to } });
}
function handleLanguageChange(index, code) {
let langs = [...languageParam];
if (index === 0) {
@ -286,7 +302,6 @@ function CollectionForm(props: Props) {
}
}, [uri, hasClaim]);
console.log('params', params);
return (
<>
<div className={classnames('main--contained', { 'card--disabled': disabled })}>
@ -357,12 +372,19 @@ function CollectionForm(props: Props) {
</div>
</TabPanel>
<TabPanel>
<ClaimList
uris={collectionUrls}
collectionId={collectionId}
empty={__('This list has no items.')}
type={'listview'}
/>
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId="list__ordering">
{(DroppableProvided) => (
<ClaimList
uris={collectionUrls}
collectionId={collectionId}
empty={__('This list has no items.')}
showEdit
droppableProvided={DroppableProvided}
/>
)}
</Droppable>
</DragDropContext>
</TabPanel>
<TabPanel>
<Card

View file

@ -0,0 +1,23 @@
import { connect } from 'react-redux';
import { doCollectionEdit } from 'redux/actions/collections';
import { makeSelectIndexForUrlInCollection, makeSelectUrlsForCollectionId } from 'redux/selectors/collections';
import CollectionButtons from './view';
const select = (state, props) => {
const { uri, collectionId } = props;
return {
collectionIndex: makeSelectIndexForUrlInCollection(uri, collectionId, true)(state),
collectionUris: makeSelectUrlsForCollectionId(collectionId)(state),
};
};
const perform = (dispatch, ownProps) => {
const { collectionId } = ownProps;
return {
editCollection: (params) => dispatch(doCollectionEdit(collectionId, params)),
};
};
export default connect(select, perform)(CollectionButtons);

View file

@ -0,0 +1,92 @@
// @flow
import * as ICONS from 'constants/icons';
import Button from 'component/button';
import Icon from 'component/common/icon';
import React from 'react';
type Props = {
collectionIndex?: number,
collectionUris: Array<Collection>,
dragHandleProps?: any,
uri: string,
editCollection: (CollectionEditParams) => void,
};
export default function CollectionButtons(props: Props) {
const { collectionIndex: foundIndex, collectionUris, dragHandleProps, uri, editCollection } = props;
const [confirmDelete, setConfirmDelete] = React.useState(false);
const lastCollectionIndex = collectionUris ? collectionUris.length - 1 : 0;
const collectionIndex = Number(foundIndex);
const orderButton = (className: string, title: string, icon: string, disabled: boolean, handleClick?: () => void) => (
<Button
className={`button-collection-manage ${className}`}
icon={icon}
title={title}
disabled={disabled}
onClick={() => handleClick && handleClick()}
/>
);
return (
<div
className="collection-preview__edit-buttons"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<div className="collection-preview__edit-group" {...dragHandleProps}>
<div className="button-collection-manage button-collection-drag top-left bottom-left">
<Icon icon={ICONS.DRAG} title={__('Drag')} size={20} />
</div>
</div>
<div className="collection-preview__edit-group">
{orderButton('', __('Move Top'), ICONS.UP_TOP, collectionIndex === 0, () =>
editCollection({ order: { from: collectionIndex, to: 0 } })
)}
{orderButton('', __('Move Bottom'), ICONS.DOWN_BOTTOM, collectionIndex === lastCollectionIndex, () =>
editCollection({ order: { from: collectionIndex, to: lastCollectionIndex } })
)}
</div>
<div className="collection-preview__edit-group">
{orderButton('', __('Move Up'), ICONS.UP, collectionIndex === 0, () =>
editCollection({ order: { from: collectionIndex, to: collectionIndex - 1 } })
)}
{orderButton('', __('Move Down'), ICONS.DOWN, collectionIndex === lastCollectionIndex, () =>
editCollection({ order: { from: collectionIndex, to: collectionIndex + 1 } })
)}
</div>
{!confirmDelete ? (
<div className="collection-preview__edit-group collection-preview__delete ">
<Button
className="button-collection-manage button-collection-delete top-right bottom-right"
icon={ICONS.DELETE}
title={__('Remove')}
onClick={() => setConfirmDelete(true)}
/>
</div>
) : (
<div className="collection-preview__edit-group collection-preview__delete">
<Button
className="button-collection-manage button-collection-delete-cancel top-right"
icon={ICONS.REMOVE}
title={__('Cancel')}
onClick={() => setConfirmDelete(false)}
/>
{orderButton('button-collection-delete-confirm bottom-right', __('Remove'), ICONS.DELETE, false, () =>
editCollection({ uris: [uri], remove: true })
)}
</div>
)}
</div>
);
}

View file

@ -5,10 +5,12 @@ import { doCollectionEdit } from 'redux/actions/collections';
import CollectionSelectItem from './view';
const select = (state, props) => {
const { collectionId, uri } = props;
return {
collection: makeSelectCollectionForId(props.collectionId)(state),
hasClaim: makeSelectCollectionForIdHasClaimUrl(props.collectionId, props.uri)(state),
collectionPending: makeSelectClaimIsPending(props.collectionId)(state),
collection: makeSelectCollectionForId(collectionId)(state),
hasClaim: makeSelectCollectionForIdHasClaimUrl(collectionId, uri)(state),
collectionPending: makeSelectClaimIsPending(collectionId)(state),
};
};

View file

@ -11,15 +11,15 @@ type Props = {
category: string,
edited: boolean,
editCollection: (string, CollectionEditParams) => void,
claim: Claim,
uri: string,
collectionPending: Collection,
};
function CollectionSelectItem(props: Props) {
const { collection, hasClaim, category, editCollection, claim, collectionPending } = props;
const { collection, hasClaim, category, editCollection, uri, collectionPending } = props;
const { name, id } = collection;
const handleChange = (e) => {
editCollection(id, { claims: [claim], remove: hasClaim });
editCollection(id, { uris: [uri], remove: hasClaim });
};
let icon;

View file

@ -382,21 +382,10 @@ export const icons = {
[ICONS.NO]: buildIcon(
<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" />
),
[ICONS.UP]: buildIcon(
<g>
<polyline transform="matrix(1,0,0,-1,0,24.707107)" points="6 9 12 15 18 9" />
</g>
),
[ICONS.UP_TOP]: buildIcon(
<g>
<path d="m6 16 6-6 6 6M6 8h12" />
</g>
),
[ICONS.DOWN_BOTTOM]: buildIcon(
<g>
<path d="m6 8 6 6 6-6M6 16h12" />
</g>
),
[ICONS.UP]: buildIcon(<polyline transform="matrix(1,0,0,-1,0,24.707107)" points="6 9 12 15 18 9" />),
[ICONS.UP_TOP]: buildIcon(<path d="m6 16 6-6 6 6M6 8h12" />),
[ICONS.DOWN_BOTTOM]: buildIcon(<path d="m6 8 6 6 6-6M6 16h12" />),
[ICONS.DRAG]: buildIcon(<path d="m8 18 4 4 4-4M4 14h16M4 10h16M8 6l4-4 4 4" />),
[ICONS.DOWN]: buildIcon(<polyline points="6 9 12 15 18 9" />),
[ICONS.FULLSCREEN]: buildIcon(
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />

View file

@ -1,18 +1,15 @@
import { connect } from 'react-redux';
import { doCollectionEdit } from 'redux/actions/collections';
import { makeSelectCollectionForIdHasClaimUrl } from 'redux/selectors/collections';
import { makeSelectClaimForUri } from 'redux/selectors/claims';
import * as COLLECTIONS_CONSTS from 'constants/collections';
import FileWatchLaterLink from './view';
import { doToast } from 'redux/actions/notifications';
const select = (state, props) => {
const claim = makeSelectClaimForUri(props.uri)(state);
const permanentUri = claim && claim.permanent_url;
const { uri } = props;
return {
claim,
hasClaimInWatchLater: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.WATCH_LATER_ID, permanentUri)(state),
hasClaimInWatchLater: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.WATCH_LATER_ID, uri)(state),
};
};

View file

@ -7,7 +7,6 @@ import * as COLLECTIONS_CONSTS from 'constants/collections';
type Props = {
uri: string,
claim: StreamClaim,
focusable: boolean,
hasClaimInWatchLater: boolean,
doToast: ({ message: string }) => void,
@ -15,14 +14,10 @@ type Props = {
};
function FileWatchLaterLink(props: Props) {
const { claim, hasClaimInWatchLater, doToast, doCollectionEdit, focusable = true } = props;
const { uri, hasClaimInWatchLater, doToast, doCollectionEdit, focusable = true } = props;
const buttonRef = useRef();
let isHovering = useHover(buttonRef);
if (!claim) {
return null;
}
function handleWatchLater(e) {
e.preventDefault();
doToast({
@ -31,7 +26,7 @@ function FileWatchLaterLink(props: Props) {
linkTarget: !hasClaimInWatchLater && '/list/watchlater',
});
doCollectionEdit(COLLECTIONS_CONSTS.WATCH_LATER_ID, {
claims: [claim],
uris: [uri],
remove: hasClaimInWatchLater,
type: 'playlist',
});

View file

@ -2,7 +2,7 @@
// https://github.com/carmelopullara/react-feather
// Note: Icons should be named for their purpose, rather than the actual icon.
// The goal being to reduce multiple uses of the same icon for different purposes.
//
export const REWARDS = 'Award';
export const LOCAL = 'Folder';
export const ALERT = 'AlertCircle';
@ -68,6 +68,7 @@ export const UP = 'ChevronUp';
export const UP_TOP = 'ChevronUpTop';
export const DOWN = 'ChevronDown';
export const DOWN_BOTTOM = 'ChevronDownBottom';
export const DRAG = 'DragAndDrop';
export const SECURE = 'Lock';
export const MENU = 'Menu';
export const BACKUP = 'Database';

View file

@ -1,4 +1,8 @@
// @flow
// $FlowFixMe
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import React from 'react';
import ClaimList from 'component/claimList';
import Page from 'component/page';
@ -51,6 +55,7 @@ export default function CollectionPage(props: Props) {
collectionHasEdits,
claimIsPending,
isResolvingCollection,
editCollection,
fetchCollectionItems,
deleteCollection,
} = props;
@ -63,9 +68,22 @@ export default function CollectionPage(props: Props) {
const [didTryResolve, setDidTryResolve] = React.useState(false);
const [showInfo, setShowInfo] = React.useState(false);
const [showEdit, setShowEdit] = React.useState(false);
const [unavailableUris, setUnavailable] = React.useState([]);
const { name, totalItems } = collection || {};
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);
const editing = urlParams.get(PAGE_VIEW_QUERY) === EDIT_PAGE;
@ -94,6 +112,18 @@ export default function CollectionPage(props: Props) {
/>
);
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;
@ -124,7 +154,7 @@ export default function CollectionPage(props: Props) {
{claim ? claim.value.title || claim.name : collection && collection.name}
</span>
}
titleActions={titleActions}
titleActions={unavailableUris.length > 0 ? removeUnavailable : titleActions}
subtitle={subTitle}
body={
<CollectionActions
@ -191,7 +221,20 @@ export default function CollectionPage(props: Props) {
<Page>
<div className={classnames('section card-stack')}>
{info}
<ClaimList uris={collectionUrls} collectionId={collectionId} type={showEdit ? 'listview' : undefined} />
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId="list__ordering">
{(DroppableProvided) => (
<ClaimList
uris={collectionUrls}
collectionId={collectionId}
showEdit={showEdit}
droppableProvided={DroppableProvided}
unavailableUris={unavailableUris}
/>
)}
</Droppable>
</DragDropContext>
</div>
</Page>
);

View file

@ -318,160 +318,49 @@ export const doCollectionEdit = (collectionId: string, params: CollectionEditPar
) => {
const state = getState();
const collection: Collection = makeSelectCollectionForId(collectionId)(state);
if (!collection) return dispatch({ type: ACTIONS.COLLECTION_ERROR, data: { message: 'collection does not exist' } });
const editedCollection: Collection = makeSelectEditedCollectionForId(collectionId)(state);
const unpublishedCollection: Collection = makeSelectUnpublishedCollectionForId(collectionId)(state);
const publishedCollection: Collection = makeSelectPublishedCollectionForId(collectionId)(state); // needs to be published only
const generateCollectionItemsFromSearchResult = (results) => {
return (
Object.values(results)
// $FlowFixMe
.reduce(
(
acc,
cur: {
stream: ?StreamClaim,
channel: ?ChannelClaim,
claimsInChannel: ?number,
collection: ?CollectionClaim,
}
) => {
let url;
if (cur.stream) {
url = cur.stream.permanent_url;
} else if (cur.channel) {
url = cur.channel.permanent_url;
} else if (cur.collection) {
url = cur.collection.permanent_url;
} else {
return acc;
}
acc.push(url);
return acc;
},
[]
)
);
};
if (!collection) {
return dispatch({
type: ACTIONS.COLLECTION_ERROR,
data: {
message: 'collection does not exist',
},
});
}
let currentItems = collection.items ? collection.items.concat() : [];
const { claims: passedClaims, order, claimIds, replace, remove, type } = params;
const { uris, order, remove, type } = params;
const collectionType = type || collection.type;
let newItems: Array<?string> = currentItems;
const currentUrls = collection.items ? collection.items.concat() : [];
let newItems = currentUrls;
if (passedClaims) {
// Passed uris to add/remove:
if (uris) {
if (remove) {
const passedUrls = passedClaims.map((claim) => claim.permanent_url);
// $FlowFixMe // need this?
newItems = currentItems.filter((item: string) => !passedUrls.includes(item));
// Filters (removes) the passed uris from the current list items
newItems = currentUrls.filter((url) => url && !uris.includes(url));
} else {
passedClaims.forEach((claim) => newItems.push(claim.permanent_url));
}
}
if (claimIds) {
const batches = [];
if (claimIds.length > 50) {
for (let i = 0; i < Math.ceil(claimIds.length / 50); i++) {
batches[i] = claimIds.slice(i * 50, (i + 1) * 50);
}
} else {
batches[0] = claimIds;
}
const resultArray = await Promise.all(
batches.map((batch) => {
let options = { claim_ids: batch, page: 1, page_size: 50 };
return dispatch(doClaimSearch(options));
})
);
const searchResults = Object.assign({}, ...resultArray);
if (replace) {
newItems = generateCollectionItemsFromSearchResult(searchResults);
} else {
newItems = currentItems.concat(generateCollectionItemsFromSearchResult(searchResults));
// Pushes (adds to the end) the passed uris to the current list items
uris.forEach((url) => newItems.push(url));
}
}
// Passed an ordering to change: (doesn't need the uris here since
// the items are already on the list)
if (order) {
const [movedItem] = currentItems.splice(order.from, 1);
currentItems.splice(order.to, 0, movedItem);
const [movedItem] = currentUrls.splice(order.from, 1);
currentUrls.splice(order.to, 0, movedItem);
}
// console.log('p&e', publishedCollection.items, newItems, publishedCollection.items.join(','), newItems.join(','))
if (editedCollection) {
// delete edited if newItems are the same as publishedItems
if (publishedCollection.items.join(',') === newItems.join(',')) {
dispatch({
type: ACTIONS.COLLECTION_DELETE,
data: {
id: collectionId,
collectionKey: 'edited',
},
});
} else {
dispatch({
type: ACTIONS.COLLECTION_EDIT,
data: {
id: collectionId,
collectionKey: 'edited',
collection: {
items: newItems,
id: collectionId,
name: params.name || collection.name,
updatedAt: getTimestamp(),
type: collectionType,
},
},
});
}
} else if (publishedCollection) {
// Delete 'edited' if newItems are the same as publishedItems
if (editedCollection && newItems && publishedCollection.items.join(',') === newItems.join(',')) {
dispatch({ type: ACTIONS.COLLECTION_DELETE, data: { id: collectionId, collectionKey: 'edited' } });
} else {
dispatch({
type: ACTIONS.COLLECTION_EDIT,
data: {
id: collectionId,
collectionKey: 'edited',
collection: {
items: newItems,
id: collectionId,
name: params.name || collection.name,
updatedAt: getTimestamp(),
type: collectionType,
},
},
});
} else if (COLS.BUILTIN_LISTS.includes(collectionId)) {
dispatch({
type: ACTIONS.COLLECTION_EDIT,
data: {
id: collectionId,
collectionKey: 'builtin',
collection: {
items: newItems,
id: collectionId,
name: params.name || collection.name,
updatedAt: getTimestamp(),
type: collectionType,
},
},
});
} else if (unpublishedCollection) {
dispatch({
type: ACTIONS.COLLECTION_EDIT,
data: {
id: collectionId,
collectionKey: 'unpublished',
collectionKey:
((editedCollection || publishedCollection) && 'edited') ||
(COLS.BUILTIN_LISTS.includes(collectionId) && 'builtin') ||
(unpublishedCollection && 'unpublished'),
collection: {
items: newItems,
id: collectionId,
@ -482,5 +371,6 @@ export const doCollectionEdit = (collectionId: string, params: CollectionEditPar
},
});
}
return true;
};

View file

@ -156,13 +156,13 @@ export const makeSelectClaimIdsForCollectionId = (id: string) =>
return ids;
});
export const makeSelectIndexForUrlInCollection = (url: string, id: string) =>
export const makeSelectIndexForUrlInCollection = (url: string, id: string, ignoreShuffle?: boolean) =>
createSelector(
(state) => state.content.shuffleList,
makeSelectUrlsForCollectionId(id),
makeSelectClaimForUri(url),
(shuffleState, urls, claim) => {
const shuffleUrls = shuffleState && shuffleState.collectionId === id && shuffleState.newUrls;
const shuffleUrls = !ignoreShuffle && shuffleState && shuffleState.collectionId === id && shuffleState.newUrls;
const listUrls = shuffleUrls || urls;
const index = listUrls && listUrls.findIndex((u) => u === url);

View file

@ -120,7 +120,7 @@
order: 1;
background-repeat: no-repeat;
background-position: center;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='14' viewBox='0 -2 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-monitor'%3E%3Crect x='2' y='3' width='20' height='14' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='8' y1='21' x2='16' y2='21'%3E%3C/line%3E%3Cline x1='12' y1='17' x2='12' y2='21'%3E%3C/line%3E%3C/svg%3E");
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='19' height='19' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-monitor'%3E%3Crect x='2' y='3' width='20' height='14' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='8' y1='21' x2='16' y2='21'%3E%3C/line%3E%3Cline x1='12' y1='17' x2='12' y2='21'%3E%3C/line%3E%3C/svg%3E");
}
&:focus:not(:focus-visible) {
@ -128,7 +128,7 @@
// see: https://github.com/lbryio/lbry-desktop/pull/5549#discussion_r580406932
background-repeat: no-repeat;
background-position: center;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='14' viewBox='0 -2 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-monitor'%3E%3Crect x='2' y='3' width='20' height='14' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='8' y1='21' x2='16' y2='21'%3E%3C/line%3E%3Cline x1='12' y1='17' x2='12' y2='21'%3E%3C/line%3E%3C/svg%3E");
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='19' height='19' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-monitor'%3E%3Crect x='2' y='3' width='20' height='14' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='8' y1='21' x2='16' y2='21'%3E%3C/line%3E%3Cline x1='12' y1='17' x2='12' y2='21'%3E%3C/line%3E%3C/svg%3E");
}
}
@ -148,26 +148,35 @@
.vjs-button--play-next.vjs-button {
order: 0;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-forward'%3e%3cpolygon points='5 4 15 12 5 20 5 4'%3e%3c/polygon%3e%3cline x1='19' y1='5' x2='19' y2='19'%3e%3c/line%3e%3c/svg%3e");
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='19' height='19' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-forward'%3e%3cpolygon points='5 4 15 12 5 20 5 4'%3e%3c/polygon%3e%3cline x1='19' y1='5' x2='19' y2='19'%3e%3c/line%3e%3c/svg%3e");
&:focus:not(:focus-visible) {
order: 0;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-forward'%3e%3cpolygon points='5 4 15 12 5 20 5 4'%3e%3c/polygon%3e%3cline x1='19' y1='5' x2='19' y2='19'%3e%3c/line%3e%3c/svg%3e");
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='19' height='19' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-forward'%3e%3cpolygon points='5 4 15 12 5 20 5 4'%3e%3c/polygon%3e%3cline x1='19' y1='5' x2='19' y2='19'%3e%3c/line%3e%3c/svg%3e");
}
}
.vjs-button--play-previous.vjs-button {
order: 0;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-back'%3e%3cpolygon points='19 20 9 12 19 4 19 20'%3e%3c/polygon%3e%3cline x1='5' y1='19' x2='5' y2='5'%3e%3c/line%3e%3c/svg%3e");
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='19' height='19' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-back'%3e%3cpolygon points='19 20 9 12 19 4 19 20'%3e%3c/polygon%3e%3cline x1='5' y1='19' x2='5' y2='5'%3e%3c/line%3e%3c/svg%3e");
&:focus:not(:focus-visible) {
order: 0;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-back'%3e%3cpolygon points='19 20 9 12 19 4 19 20'%3e%3c/polygon%3e%3cline x1='5' y1='19' x2='5' y2='5'%3e%3c/line%3e%3c/svg%3e");
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='19' height='19' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-back'%3e%3cpolygon points='19 20 9 12 19 4 19 20'%3e%3c/polygon%3e%3cline x1='5' y1='19' x2='5' y2='5'%3e%3c/line%3e%3c/svg%3e");
}
}
.vjs-button--autoplay-next.vjs-button {
margin: auto;
// TODO: the width and height of a vjs-button should probably retain
// its default "100%" value, so that each control-bar button have the same
// touch area size. Anything inside (like this on/off switch, or icons)
// should be a child (e.g. vjs-icon-placeholder), so that inner margins will
// also be consistent.
//
// TEMP: for now, just hardcode the left-right margin so that it looks
// equally spaced compared to its siblings.
margin: auto 0.5rem auto 0.7rem;
order: 1;
width: 24px;
height: 14px;
@ -303,6 +312,7 @@
}
}
// REMOVE ?
.button--file-action-active {
.icon {
fill: var(--color-primary-alt);
@ -616,16 +626,17 @@ svg + .button__label {
background-color: var(--color-button-alt-bg);
color: var(--color-button-alt-text);
text-align: center;
&:hover {
background-color: var(--color-button-alt-bg-hover);
}
@media (max-width: $breakpoint-small) {
font-size: var(--font-small);
}
@media (max-width: $breakpoint-small) {
padding: var(--spacing-s) var(--spacing-s);
&:focus {
background-color: var(--color-button-alt-bg);
}
}
&.top-right {
@ -648,6 +659,15 @@ svg + .button__label {
&.button-collection-delete-cancel {
background-color: red;
}
&.button-collection-drag {
cursor: grab;
display: flex;
align-content: center;
justify-content: center;
align-items: center;
padding-bottom: var(--spacing-xs);
}
}
.button-toggle--expandformobile {

View file

@ -216,8 +216,12 @@
}
.claim-preview--collection-mine {
padding-left: 7rem;
padding-left: 9rem;
position: relative;
@media (max-width: $breakpoint-small) {
padding-left: 6.5rem;
}
}
.claim-preview--pending {
@ -240,7 +244,6 @@
.claim-preview__actions {
align-self: flex-end;
margin-bottom: auto;
justify-content: flex-end;
width: auto;
}
@ -250,10 +253,6 @@
justify-content: space-between;
flex-wrap: wrap;
align-items: center;
.claim-preview__actions {
margin-left: var(--spacing-m);
}
}
@media (max-width: $breakpoint-xsmall) {
@ -302,6 +301,14 @@
flex: 1;
}
// REMOVE ?
.claim-preview-metadata-sub-upload {
position: relative;
//margin-left: 4px;
display: flex;
text-align: left;
}
.claim-preview-info {
align-items: flex-start;
}

View file

@ -94,7 +94,7 @@
padding-top: var(--spacing-m);
padding-bottom: var(--spacing-m);
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr;
align-items: center;
}
@ -107,6 +107,10 @@
justify-content: center;
align-items: stretch;
flex-grow: 4;
@media (max-width: $breakpoint-small) {
width: 1.5rem;
}
}
.collection-edit__header {

View file

@ -12,7 +12,7 @@
display: flex;
align-items: flex-start;
justify-content: space-between;
min-height: calc(100vh - var(--header-height));
min-height: 100vh;
margin-left: auto;
margin-right: auto;
margin-top: var(--header-height);
@ -26,7 +26,7 @@
@media (max-width: $breakpoint-small) {
padding: var(--spacing-xs);
padding-top: var(--spacing-m);
margin-top: var(--header-height-mobile);
}
@media (min-width: $breakpoint-large) {
@ -42,20 +42,52 @@
padding-top: 0;
}
// REMOVE next 4?
.main-wrapper__inner--auth {
min-height: unset;
padding: 0;
}
.sidebar--pusher {
animation-timing-function: var(--resizing-animation-function);
transition: transform var(--resizing-animation-timing);
transform-origin: left;
position: absolute;
@media (max-width: $breakpoint-small) {
transform: translateX(0);
width: calc(100% - var(--spacing-m));
}
@media (min-width: $breakpoint-small) {
transform: translateX(var(--side-nav-width--micro));
width: calc(100% - ((var(--side-nav-width--micro))));
}
}
.sidebar--pusher--filepage {
width: 100%;
}
.sidebar--pusher--open {
@media (min-width: $breakpoint-medium) {
transform: translateX(var(--side-nav-width));
width: calc(100% - var(--side-nav-width));
}
}
.main {
position: relative;
width: calc(100% - var(--side-nav-width) - var(--spacing-l));
width: calc(100% - 2 * var(--spacing-l));
max-width: var(--page-max-width);
z-index: 0;
margin-right: auto;
margin-left: auto;
min-height: calc(100vh - var(--header-height));
@media (max-width: $breakpoint-medium) and (min-width: $breakpoint-small) {
margin: 0 var(--spacing-l);
}
@media (max-width: $breakpoint-medium) {
@media (max-width: $breakpoint-small) {
width: 100%;
min-height: calc(100vh - var(--header-height-mobile));
}
}
@ -127,13 +159,7 @@
margin-left: var(--spacing-m);
.card__header--between {
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) var(--spacing-s);
display: flex;
align-items: center;
.card__title-actions {
padding: 0 0 0 0;
align-items: center;
}
}
@media (max-width: $breakpoint-medium) {
width: 100%;
@ -148,7 +174,18 @@
overflow-wrap: break-word;
.card__header--between {
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) var(--spacing-s);
align-items: flex-start !important;
.card__title-actions {
display: flex;
flex-direction: column;
align-items: flex-end;
padding: var(--spacing-s);
button {
margin-top: var(--spacing-xxs);
}
}
}
.file-page__recommended-collection__row {
@ -162,6 +199,15 @@
max-width: 50rem;
}
}
.collection-preview__edit-group {
width: 1.5rem;
}
.claim-preview--collection-mine {
padding-left: 6.5rem;
position: relative;
}
}
@media (max-width: $breakpoint-medium) {
@ -221,12 +267,7 @@
.main--full-width {
@extend .main;
@media (min-width: $breakpoint-large) {
max-width: none;
width: 100%;
margin: 0 var(--spacing-l);
}
max-width: none;
}
.main--auth-page {
@ -250,6 +291,10 @@
margin-top: var(--spacing-m);
padding: 0 var(--spacing-m);
@media (max-width: $breakpoint-small) {
padding: 0 0;
}
.card__subtitle {
margin: 0 0 var(--spacing-s) 0;
font-size: var(--font-small);
@ -393,18 +438,25 @@
.main__sign-up--graphic {
max-width: 47rem;
.card__first-pane {
width: 50%;
@media (max-width: $breakpoint-small) {
width: 100%;
}
}
.card__second-pane {
width: 50%;
@media (max-width: $breakpoint-small) {
width: 100%;
}
border: none;
display: flex;
align-items: center;
justify-content: center;
background: var(--color-login-graphic-background);
@media (max-width: $breakpoint-small) {
width: 100%;
}
.signup-image {
@media (max-width: $breakpoint-small) {
width: 50%;
@ -413,7 +465,7 @@
}
.card__title {
font-size: var(--font-heading);
font-size: var(--font-title);
font-weight: var(--font-weight-bold);
}

View file

@ -4,14 +4,20 @@ export function toCapitalCase(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
export function toCompactNotation(number: string | number, lang: ?string) {
try {
return Number(number).toLocaleString(lang || 'en', {
compactDisplay: 'short',
notation: 'compact',
});
} catch (err) {
// Not all browsers support the addition options.
return Number(number).toLocaleString();
export function toCompactNotation(number: string | number, lang: ?string, minThresholdToApply?: string | number) {
const locale = lang || 'en';
if (minThresholdToApply && Number(number) >= Number(minThresholdToApply)) {
try {
return Number(number).toLocaleString(locale, {
compactDisplay: 'short',
notation: 'compact',
});
} catch (err) {
// Not all browsers support the additional options.
return Number(number).toLocaleString(locale);
}
} else {
return Number(number).toLocaleString(locale);
}
}

395
yarn.lock
View file

@ -1167,7 +1167,14 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5":
version "7.14.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
@ -1181,6 +1188,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.15.4", "@babel/runtime@^7.9.2":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.10.1", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811"
@ -1287,9 +1301,9 @@
to-fast-properties "^2.0.0"
"@datapunt/matomo-tracker-js@^0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@datapunt/matomo-tracker-js/-/matomo-tracker-js-0.1.4.tgz#1226f0964d2c062bf9392e9c2fd89838262b10df"
integrity sha512-bi8/guszgciSNLJQIFgph27AzkiCF1DmLBxtmJE3CsLxfc0aTgI2vMg3EFoLv13Mu8HLaCs27Z7vbttlD6jp5w==
version "0.1.5"
resolved "https://registry.yarnpkg.com/@datapunt/matomo-tracker-js/-/matomo-tracker-js-0.1.5.tgz#650396e2d8d9d905c04c94e637cbc21c07648d02"
integrity sha512-4L4y5XlVVfJYeyUY1F3iuG55tWBdM5xODtOJSc4LmxTIeTVMKPdI8VbPG3h/2BzO1xxk/p71XakGlemYzn0inw==
"@develar/schema-utils@~2.6.5":
version "2.6.5"
@ -1552,9 +1566,9 @@
fastq "^1.6.0"
"@npmcli/fs@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.0.0.tgz#589612cfad3a6ea0feafcb901d29c63fd52db09f"
integrity sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==
version "1.1.0"
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.0.tgz#bec1d1b89c170d40e1b73ad6c943b0b75e7d2951"
integrity sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==
dependencies:
"@gar/promisify" "^1.0.1"
semver "^7.3.5"
@ -1964,6 +1978,14 @@
"@types/minimatch" "*"
"@types/node" "*"
"@types/hoist-non-react-statics@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/html-minifier-terser@^5.0.0":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50"
@ -2000,9 +2022,9 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.0.tgz#5b6ee7a77faacddd7de719017d0bc12f52f81589"
"@types/node@^14.6.2":
version "14.18.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.1.tgz#459886b51f52aa923dc06b9ea81cb8b1d733e9d3"
integrity sha512-fTFWOFrgAkj737w1o0HLTIgisgYHnsZfeiqhG1Ltrf/iJjudEbUwetQAsfrtVE49JGwvpEzQR+EbMkIqG4227g==
version "14.18.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.9.tgz#0e5944eefe2b287391279a19b407aa98bd14436d"
integrity sha512-j11XSuRuAlft6vLDEX4RvhqC0KxNxx6QIyMXNb0vHHSNPXTPeiy3algESWmOOIzEtiEL0qiowPU3ewW9hHVa7Q==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@ -2034,6 +2056,16 @@
dependencies:
"@types/react" "*"
"@types/react-redux@^7.1.20":
version "7.1.21"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.21.tgz#f32bbaf7cbc4b93f51e10d340aa54035c084b186"
integrity sha512-bLdglUiBSQNzWVVbmNPKGYYjrzp3/YDPwfOH3nLEz99I4awLlaRAPWjo6bZ2POpxztFWtDDXIPxBLVykXqBt+w==
dependencies:
"@types/hoist-non-react-statics" "^3.3.0"
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react-transition-group@^4.4.4":
version "4.4.4"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
@ -2143,20 +2175,52 @@
resolved "https://registry.yarnpkg.com/@ungap/from-entries/-/from-entries-0.2.1.tgz#7e86196b8b2e99d73106a8f25c2a068326346354"
integrity sha512-CAqefTFAfnUPwYqsWHXpOxHaq1Zo5UQ3m9Zm2p09LggGe57rqHoBn3c++xcoomzXKynAUuiBMDUCQvKMnXjUpA==
"@videojs/http-streaming@2.9.2":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-2.9.2.tgz#47d33bb02bd9c1287200398b1e85d213dee814d0"
integrity sha512-2ZsxJn4/nZZ6k6jIhic2l9ynGmKwprtuI5b3+M6JgqOSLvQQ/ah+heVs/0g2Ze7qJxodqR+aSY948JwJIz1gCw==
"@videojs/http-streaming@2.10.2":
version "2.10.2"
resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-2.10.2.tgz#02e6fcfa74f7850b5f9eb40a8e5c85c9d7d33eaf"
integrity sha512-JTAlAUHzj0sTsge2WBh4DWKM2I5BDFEZYOvzxmsK/ySILmI0GRyjAHx9uid68ZECQ2qbEAIRmZW5lWp0R5PeNA==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/vhs-utils" "^3.0.2"
"@videojs/vhs-utils" "3.0.3"
aes-decrypter "3.1.2"
global "^4.4.0"
m3u8-parser "4.7.0"
mpd-parser "0.17.0"
mux.js "5.12.2"
mpd-parser "0.19.0"
mux.js "5.13.0"
video.js "^6 || ^7"
"@videojs/http-streaming@2.12.0":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-2.12.0.tgz#850069e063e26cf2fa5ed9bb3addfc92fa899f78"
integrity sha512-vdQA0lDYBXGJqV2T02AGqg1w4dcgyRoN+bYG+G8uF4DpCEMhEtUI0BA4tRu4/Njar8w/9D5k0a1KX40pcvM3fA==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/vhs-utils" "3.0.4"
aes-decrypter "3.1.2"
global "^4.4.0"
m3u8-parser "4.7.0"
mpd-parser "0.19.2"
mux.js "5.14.1"
video.js "^6 || ^7"
"@videojs/vhs-utils@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-3.0.3.tgz#708bc50742e9481712039695299b32da6582ef92"
integrity sha512-bU7daxDHhzcTDbmty1cXjzsTYvx2cBGbA8hG5H2Gvpuk4sdfuvkZtMCwtCqL59p6dsleMPspyaNS+7tWXx2Y0A==
dependencies:
"@babel/runtime" "^7.12.5"
global "^4.4.0"
url-toolkit "^2.2.1"
"@videojs/vhs-utils@3.0.4", "@videojs/vhs-utils@^3.0.3":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-3.0.4.tgz#e253eecd8e9318f767e752010d213587f94bb03a"
integrity sha512-hui4zOj2I1kLzDgf8QDVxD3IzrwjS/43KiS8IHQO0OeeSsb4pB/lgNt1NG7Dv0wMQfCccUpMVLGcK618s890Yg==
dependencies:
"@babel/runtime" "^7.12.5"
global "^4.4.0"
url-toolkit "^2.2.1"
"@videojs/vhs-utils@^3.0.0", "@videojs/vhs-utils@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-3.0.2.tgz#0203418ecaaff29bc33c69b6ad707787347b7614"
@ -2166,10 +2230,10 @@
global "^4.4.0"
url-toolkit "^2.2.1"
"@videojs/xhr@2.5.1":
version "2.5.1"
resolved "https://registry.yarnpkg.com/@videojs/xhr/-/xhr-2.5.1.tgz#26bc5a79dbb3b03bfb13742c6ce559f89e90719e"
integrity sha512-wV9nGESHseSK+S9ePEru2+OJZ1jq/ZbbzniGQ4weAmTIepuBMSYPx5zrxxQA0E786T5ykpO8ts+LayV+3/oI2w==
"@videojs/xhr@2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@videojs/xhr/-/xhr-2.6.0.tgz#cd897e0ad54faf497961bcce3fa16dc15a26bb80"
integrity sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==
dependencies:
"@babel/runtime" "^7.5.5"
global "~4.4.0"
@ -2325,6 +2389,11 @@
resolved "https://registry.yarnpkg.com/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz#2dcfd92881425c5923e429c2aec86fb3609032a1"
integrity sha512-1VPkkTBk07gMR1fjpBtse4G+oJqpmE+0gUFB0dg3VIL7qJmUVaBoD/vlzMm/jNeOPfvlmerl1lpnsZyBUFIRuw==
"@xmldom/xmldom@^0.7.2":
version "0.7.5"
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d"
integrity sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@ -3719,14 +3788,10 @@ codemirror-spell-checker@1.1.2:
dependencies:
typo-js "*"
codemirror@^5.39.2:
version "5.52.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.52.0.tgz#4dbd6aef7f0e63db826b9a23922f0c03ac75c0a7"
codemirror@^5.58.2:
version "5.59.2"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.59.2.tgz#ee674d3a4a8d241af38d52afc482625ba7393922"
integrity sha512-/D5PcsKyzthtSy2NNKCyJi3b+htRkoKv3idswR/tR6UAvMNKA7SrmyZy6fOONJxSRs1JlUWEDAbxqfdArbK8iA==
codemirror@^5.39.2, codemirror@^5.58.2:
version "5.63.3"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.63.3.tgz#97042a242027fe0c87c09b36bc01931d37b76527"
integrity sha512-1C+LELr+5grgJYqwZKqxrcbPsHFHapVaVAloBsFBASbpLnQqLw1U8yXJ3gT5D+rhxIiSpo+kTqN+hQ+9ialIXw==
collapse-white-space@^1.0.2:
version "1.0.6"
@ -4177,6 +4242,13 @@ crypto-random-string@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
css-box-model@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
dependencies:
tiny-invariant "^1.0.6"
css-color-names@0.0.4, css-color-names@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@ -4258,15 +4330,15 @@ css-select@^2.0.0:
nth-check "^1.0.2"
css-select@^4.1.3:
version "4.2.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.0.tgz#ab28276d3afb00cc05e818bd33eb030f14f57895"
integrity sha512-6YVG6hsH9yIb/si3Th/is8Pex7qnVHO6t7q7U6TIUnkQASGbS8tnUDBftnPynLNnuUl/r2+PTd0ekiiq7R0zJw==
version "4.1.3"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067"
integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==
dependencies:
boolbase "^1.0.0"
css-what "^5.1.0"
domhandler "^4.3.0"
domutils "^2.8.0"
nth-check "^2.0.1"
css-what "^5.0.0"
domhandler "^4.2.0"
domutils "^2.6.0"
nth-check "^2.0.0"
css-tree@1.0.0-alpha.37:
version "1.0.0-alpha.37"
@ -4283,10 +4355,10 @@ css-what@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1"
css-what@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe"
integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==
css-what@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==
cssesc@^3.0.0:
version "3.0.0"
@ -4745,10 +4817,10 @@ domhandler@^3.0.0, domhandler@^3.2.0:
dependencies:
domelementtype "^2.0.1"
domhandler@^4.2.0, domhandler@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626"
integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==
domhandler@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
dependencies:
domelementtype "^2.2.0"
@ -4775,10 +4847,10 @@ domutils@^2.0.0:
domelementtype "^2.0.1"
domhandler "^3.2.0"
domutils@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
domutils@^2.6.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442"
integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
@ -5722,10 +5794,10 @@ fast-diff@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
fast-glob@^3.1.1, fast-glob@^3.2.4:
version "3.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==
fast-glob@^3.2.4, fast-glob@^3.2.9:
version "3.2.11"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
@ -5998,9 +6070,9 @@ flush-write-stream@^1.0.0:
readable-stream "^2.3.6"
follow-redirects@^1.0.0:
version "1.14.7"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
version "1.13.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
for-in@^1.0.2:
version "1.0.2"
@ -6348,15 +6420,15 @@ globalthis@^1.0.1:
define-properties "^1.1.3"
globby@^11.0.1:
version "11.0.4"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5"
integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==
version "11.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.1.1"
ignore "^5.1.4"
merge2 "^1.3.0"
fast-glob "^3.2.9"
ignore "^5.2.0"
merge2 "^1.4.1"
slash "^3.0.0"
globby@^6.1.0:
@ -6912,10 +6984,10 @@ ignore@^5.0.2:
version "5.1.4"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf"
ignore@^5.1.4:
version "5.1.9"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb"
integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==
ignore@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
imagesloaded@^4.1.4:
version "4.1.4"
@ -8360,12 +8432,17 @@ merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
merge-refs@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/merge-refs/-/merge-refs-1.0.0.tgz#388348bce22e623782c6df9d3c4fc55888276120"
integrity sha512-WZ4S5wqD9FCR9hxkLgvcHJCBxzXzy3VVE6p8W2OzxRzB+hLRlcadGE2bW9xp2KSzk10rvp4y+pwwKO6JQVguMg==
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
merge2@^1.3.0:
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
@ -8631,15 +8708,25 @@ move-concurrently@^1.0.1:
rimraf "^2.5.4"
run-queue "^1.0.3"
mpd-parser@0.17.0:
version "0.17.0"
resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-0.17.0.tgz#d7f3002edcb706f98993ef75846a713d056d3332"
integrity sha512-oKS5G0jCcHHJ3sHYlcLeM9Xcbuixl08eAx7QW0Th7ChlZiI0YvLtGaHE/L0aKUBJFNvtkeksIr8XgJgSBBsS4g==
mpd-parser@0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-0.19.0.tgz#8937044040ca85e20398ecf5d94552655e2c6728"
integrity sha512-FDLIXtZMZs99fv5iXNFg94quNFT26tobo0NUgHu7L3XgZvEq1NBarf5yxDFFJ1zzfbcmtj+NRaml6nYIxoPWvw==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/vhs-utils" "^3.0.2"
"@xmldom/xmldom" "^0.7.2"
global "^4.4.0"
mpd-parser@0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-0.19.2.tgz#68611e653cdf2cc1e90688825c4a129b7f9007e0"
integrity sha512-M5tAIdtBM2TN+OSTz/37T7V+h9ZLvhyNqq4TNIdtjAQ/Hg8UnMRf5nJQDjffcXag3POXi31yUJQEKOXdcAM/nw==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/vhs-utils" "^3.0.2"
"@xmldom/xmldom" "^0.7.2"
global "^4.4.0"
xmldom "^0.5.0"
ms@2.0.0:
version "2.0.0"
@ -8669,10 +8756,17 @@ mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
mux.js@5.12.2:
version "5.12.2"
resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-5.12.2.tgz#cd823312f4bb69adb8b9c5f45635b4451066d6e6"
integrity sha512-9OY1lrFIo7FxMeIC6aLUftiNv97AztufDfi30N7qDll1Pcy7bCxlHztyHp1Ce0KQwy2XqchGeENPS4v1NJngHQ==
mux.js@5.13.0:
version "5.13.0"
resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-5.13.0.tgz#99c3da21f6c0362a1529729d1c5e5b51f34f606d"
integrity sha512-PkmnzHcTQjUBEHp3KRPQAFoNkJtKlpCEvsYtXDfDrC+/WqbMuxHvoYfmAbHVAH7Sa/KliPVU0dT1ureO8wilog==
dependencies:
"@babel/runtime" "^7.11.2"
mux.js@5.14.1:
version "5.14.1"
resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-5.14.1.tgz#209583f454255d9ba2ff1bb61ad5a6867cf61878"
integrity sha512-38kA/xjWRDzMbcpHQfhKbJAME8eTZVsb9U2Puk890oGvGqnyu8B/AkKdICKPHkigfqYX9MY20vje88TP14nhog==
dependencies:
"@babel/runtime" "^7.11.2"
@ -8742,9 +8836,9 @@ node-forge@0.9.0:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
node-html-parser@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.1.0.tgz#753f5a60cdfe6d027c15857cb817df592c18c998"
integrity sha512-l6C1Gf1o7YuxeMGa17PypEez/rj+ii3q4/NZG37nRmWSLDjHyB0WNrlE4h2UW92D0JSfUSfu+lOvxThttVe7Jw==
version "5.2.0"
resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.2.0.tgz#6f29fd00d79f65334e7e20200964644207925607"
integrity sha512-fmiwLfQu+J2A0zjwSEkztSHexAf5qq/WoiL/Hgo1K7JpfEP+OGWY5maG0kGaM+IFVdixF/1QbyXaQ3h4cGfeLw==
dependencies:
css-select "^4.1.3"
he "1.2.0"
@ -8916,10 +9010,10 @@ nth-check@^1.0.2, nth-check@~1.0.1:
dependencies:
boolbase "~1.0.0"
nth-check@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==
nth-check@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
dependencies:
boolbase "^1.0.0"
@ -9437,9 +9531,9 @@ picomatch@^2.0.4, picomatch@^2.2.1:
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
picomatch@^2.2.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pify@^2.0.0, pify@^2.3.0:
version "2.3.0"
@ -10134,6 +10228,11 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
raf-schd@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@ -10201,6 +10300,19 @@ react-awesome-lightbox@^1.7.3:
resolved "https://registry.yarnpkg.com/react-awesome-lightbox/-/react-awesome-lightbox-1.7.3.tgz#ee1c00fd4197e0e65bf996aa219eac4d8b6db5a0"
integrity sha512-mSxdL3KGzuh2eR8I00nv9njiolmMoXITuCvfd71DBXK13JW3e+Z/sCMENS9+dngBJU8/m7dR1Ix0W6afS5cFsA==
react-beautiful-dnd@^13.1.0:
version "13.1.0"
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz#ec97c81093593526454b0de69852ae433783844d"
integrity sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==
dependencies:
"@babel/runtime" "^7.9.2"
css-box-model "^1.2.0"
memoize-one "^5.1.1"
raf-schd "^4.0.2"
react-redux "^7.2.0"
redux "^4.0.4"
use-memo-one "^1.1.1"
react-calendar@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/react-calendar/-/react-calendar-3.3.1.tgz#da691a5d59c88f178695fd8b33909a71d698021f"
@ -10236,25 +10348,26 @@ react-confetti@^4.0.1:
dependencies:
tween-functions "^1.2.0"
react-date-picker@^8.1.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/react-date-picker/-/react-date-picker-8.1.1.tgz#1959608cd042c9bfcf2faa6d63a56e9ef6b17e2b"
integrity sha512-kFhn+uSJML+EuROvR6qLYU5G3wsxrdB2K1ugh1t6HjJCjphE6ot85jb8THWebqWEcQi07pLseU7ZFpzKDD3A6A==
react-date-picker@^8.3.3:
version "8.3.6"
resolved "https://registry.yarnpkg.com/react-date-picker/-/react-date-picker-8.3.6.tgz#446142bee5691aea66a2bac53313357aca561cd4"
integrity sha512-c1rThf0jSKROoSGLpUEPtcC8VE+XoVgqxh+ng9aLYQvjDMGWQBgoat6Qrj8nRVzvCPpdXV4jqiCB3z2vVVuseA==
dependencies:
"@types/react-calendar" "^3.0.0"
"@wojtekmaj/date-utils" "^1.0.3"
get-user-locale "^1.2.0"
make-event-props "^1.1.0"
merge-class-names "^1.1.1"
merge-refs "^1.0.0"
prop-types "^15.6.0"
react-calendar "^3.3.1"
react-fit "^1.0.3"
update-input-width "^1.1.1"
update-input-width "^1.2.2"
react-datetime-picker@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/react-datetime-picker/-/react-datetime-picker-3.2.1.tgz#d3a9631bcba17bd0047e6424cff0dfe242d9cf0e"
integrity sha512-elybaAL7RJG7r0elYZze5/zQo1ds0v+v89tyZkzEShw+6I1EcveXwYPOMj3aq0k7D5kY/K+dC5dWYw0w4d9kmw==
react-datetime-picker@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/react-datetime-picker/-/react-datetime-picker-3.4.3.tgz#9163471f72b708185482b6b72cd259da03462f79"
integrity sha512-yuFmh3TJwDo3VnyQF6auRJoeYfFTUtyLsR292lWXieigp0ugKkQefUEzVybZQidiiUlCNK9UQgc37/igl7uBYA==
dependencies:
"@wojtekmaj/date-utils" "^1.0.3"
get-user-locale "^1.2.0"
@ -10263,9 +10376,9 @@ react-datetime-picker@^3.2.1:
prop-types "^15.6.0"
react-calendar "^3.3.1"
react-clock "^3.0.0"
react-date-picker "^8.1.0"
react-date-picker "^8.3.3"
react-fit "^1.0.3"
react-time-picker "^4.2.0"
react-time-picker "^4.4.2"
react-dom@^16.8.2:
version "16.13.0"
@ -10356,6 +10469,18 @@ react-redux@^6.0.1:
prop-types "^15.7.2"
react-is "^16.8.2"
react-redux@^7.2.0:
version "7.2.6"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa"
integrity sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==
dependencies:
"@babel/runtime" "^7.15.4"
"@types/react-redux" "^7.1.20"
hoist-non-react-statics "^3.3.2"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-is "^17.0.2"
react-router-dom@^5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"
@ -10399,19 +10524,20 @@ react-spring@^8.0.20, react-spring@^8.0.27:
"@babel/runtime" "^7.3.1"
prop-types "^15.5.8"
react-time-picker@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/react-time-picker/-/react-time-picker-4.2.1.tgz#b27f0bbc2e58534f20dbf10b14d0b8f3334fcb07"
integrity sha512-T0aEabJ3bz54l8LV3pdpB5lOZuO3pRIbry5STcUV58UndlrWLcHpdpvS1IC8JLNXhbLxzGs1MmpASb5k1ddlsg==
react-time-picker@^4.4.2:
version "4.4.4"
resolved "https://registry.yarnpkg.com/react-time-picker/-/react-time-picker-4.4.4.tgz#a67ca5fd88f51eac0919df802e416d9a25ad726a"
integrity sha512-WMdrpGnegug0871Do+SU1Fe91uZGmS6JUo1Yw7eLfU3VHMXCFj9sL9FAT6BuXe7lfILBbXq4tQQOqa/rLDASQg==
dependencies:
"@wojtekmaj/date-utils" "^1.0.0"
get-user-locale "^1.2.0"
make-event-props "^1.1.0"
merge-class-names "^1.1.1"
merge-refs "^1.0.0"
prop-types "^15.6.0"
react-clock "^3.0.0"
react-fit "^1.0.3"
update-input-width "^1.1.1"
update-input-width "^1.2.2"
react-transition-group@^4.4.2:
version "4.4.2"
@ -10606,6 +10732,13 @@ redux@^3.6.0:
loose-envify "^1.1.0"
symbol-observable "^1.0.3"
redux@^4.0.0, redux@^4.0.4:
version "4.1.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104"
integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==
dependencies:
"@babel/runtime" "^7.9.2"
regenerate-unicode-properties@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
@ -12172,6 +12305,11 @@ tiny-invariant@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
tiny-invariant@^1.0.6:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==
tiny-relative-date@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07"
@ -12646,10 +12784,10 @@ upath@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
update-input-width@^1.1.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/update-input-width/-/update-input-width-1.2.1.tgz#769d6182413590c3b50b52ffa9c65d79e2c17f95"
integrity sha512-zygDshqDb2C2/kgfoD423n5htv/3OBF7aTaz2u2zZy998EJki8njOHOeZjKEd8XSYeDziIX1JXfMsKaIRJeJ/Q==
update-input-width@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/update-input-width/-/update-input-width-1.2.2.tgz#9a6a35858ae8e66fbfe0304437b23a4934fc7d37"
integrity sha512-6QwD9ZVSXb96PxOZ01DU0DJTPwQGY7qBYgdniZKJN02Xzom2m+9J6EPxMbefskqtj4x78qbe5psDSALq9iNEYg==
update-notifier@^2.5.0:
version "2.5.0"
@ -12741,6 +12879,11 @@ url@^0.11.0:
punycode "1.3.2"
querystring "0.2.0"
use-memo-one@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20"
integrity sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
@ -12862,21 +13005,40 @@ vfile@^2.0.0:
unist-util-stringify-position "^1.0.0"
vfile-message "^1.0.0"
"video.js@^6 || ^7", video.js@^7.0.0, video.js@^7.14.3:
version "7.14.3"
resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.14.3.tgz#0b612c09a0a81ef9bce65c710e73291cb06dc32c"
integrity sha512-6avCdSIfn5ss5NOgoQfY/xEfPNcz9DXSw+ZN80NwPguCdRd4VL4y40b/d7osYJwyCdF+YkvhqAW7dw4s0vBigg==
"video.js@^6 || ^7", video.js@^7.0.0:
version "7.15.4"
resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.15.4.tgz#0f96ef138035138cb30bf00a989b6174f0d16bac"
integrity sha512-hghxkgptLUvfkpktB4wxcIVF3VpY/hVsMkrjHSv0jpj1bW9Jplzdt8IgpTm9YhlB1KYAp07syVQeZcBFUBwhkw==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/http-streaming" "2.9.2"
"@videojs/vhs-utils" "^3.0.2"
"@videojs/xhr" "2.5.1"
"@videojs/http-streaming" "2.10.2"
"@videojs/vhs-utils" "^3.0.3"
"@videojs/xhr" "2.6.0"
aes-decrypter "3.1.2"
global "^4.4.0"
keycode "^2.2.0"
m3u8-parser "4.7.0"
mpd-parser "0.17.0"
mux.js "5.12.2"
mpd-parser "0.19.0"
mux.js "5.13.0"
safe-json-parse "4.0.0"
videojs-font "3.2.0"
videojs-vtt.js "^0.15.3"
video.js@^7.14.3:
version "7.17.0"
resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.17.0.tgz#35918cc03748a5680f5d5f1da410e06eeea7786e"
integrity sha512-8RbLu9+Pdpep9OTPncUHIvZXFgn/7hKdPnSTE/lGSnlFSucXtTUBp41R7NDwncscMLQ0WgazUbmFlvr4MNWMbA==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/http-streaming" "2.12.0"
"@videojs/vhs-utils" "^3.0.3"
"@videojs/xhr" "2.6.0"
aes-decrypter "3.1.2"
global "^4.4.0"
keycode "^2.2.0"
m3u8-parser "4.7.0"
mpd-parser "0.19.2"
mux.js "5.14.1"
safe-json-parse "4.0.0"
videojs-font "3.2.0"
videojs-vtt.js "^0.15.3"
@ -13363,11 +13525,6 @@ xmldom@^0.3.0:
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a"
integrity sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g==
xmldom@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e"
integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==
xpipe@*:
version "1.0.5"
resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf"