diff --git a/.gitignore b/.gitignore index c7c46d6fb..0c609ba47 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,12 @@ package-lock.json /web/.env.defaults /custom/* /custom/homepages/* +/custom/content/* +!/custom/content/default.json +!/custom/content/test.json !/custom/homepages/.gitkeep !/custom/homepages +!/custom/content !/custom/homepage.example.js !/custom/robots.disallowall !/custom/robots.allowall diff --git a/package.json b/package.json index 95ed3570b..e1e3abab0 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "imagesloaded": "^4.1.4", "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", - "lbry-redux": "lbryio/lbry-redux#ecfcc95bebbdbe303b3ea065134457a5e168fb89", + "lbry-redux": "lbryio/lbry-redux#5cd9e7601cdc1c81b8d0e2d2f02e9b9d2fb86575", "lbryinc": "lbryio/lbryinc#8f9a58bfc8312a65614fd7327661cdcc502c4e59", "lint-staged": "^7.0.2", "localforage": "^1.7.1", diff --git a/static/app-strings.json b/static/app-strings.json index f97094da7..b6dafae38 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1917,5 +1917,35 @@ "Set to current date and time": "Set to current date and time", "Remove custom release date": "Remove custom release date", "%SITE_NAME% login": "%SITE_NAME% login", + "Lists": "Lists", + "Watch Later": "Watch Later", + "Add to Lists": "Add to Lists", + "Nothing in %collection_name%": "Nothing in %collection_name%", + "Playlists": "Playlists", + "Edit List": "Edit List", + "Delete List": "Delete List", + "Private": "Private", + "View List": "View List", + "Delete Collection": "Delete Collection", + "Info": "Info", + "Publishes": "Publishes", + "Add To...": "Add To...", + "Unpublished Edits": "Unpublished Edits", + "Report channel": "Report channel", + "List": "List", + "Items": "Items", + "Credits": "Credits", + "MyAwesomeCollection": "MyAwesomeCollection", + "My Awesome Collection": "My Awesome Collection", + "This collection has no items.": "This collection has no items.", + "Select File": "Select File", + "File Selected": "File Selected", + "Url": "Url", + "URL Selected": "URL Selected", + "Keep": "Keep", + "Add this claim to a list": "Add this claim to a list", + "List is Empty": "List is Empty", + "Confirm Collection Unpublish": "Confirm Collection Unpublish", + "This will permanently delete the list.": "This will permanently delete the list.", "--end--": "--end--" } diff --git a/ui/component/app/index.js b/ui/component/app/index.js index 84b62c9f9..16b0cd288 100644 --- a/ui/component/app/index.js +++ b/ui/component/app/index.js @@ -5,7 +5,7 @@ import { selectGetSyncErrorMessage, selectSyncFatalError } from 'redux/selectors import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user'; import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectUnclaimedRewards } from 'redux/selectors/rewards'; -import { doFetchChannelListMine, selectMyChannelUrls, SETTINGS } from 'lbry-redux'; +import { doFetchChannelListMine, doFetchCollectionListMine, SETTINGS, selectMyChannelUrls } from 'lbry-redux'; import { makeSelectClientSetting, selectLanguage, @@ -52,6 +52,7 @@ const select = (state) => ({ const perform = (dispatch) => ({ fetchAccessToken: () => dispatch(doFetchAccessToken()), fetchChannelListMine: () => dispatch(doFetchChannelListMine()), + fetchCollectionListMine: () => dispatch(doFetchCollectionListMine()), setLanguage: (language) => dispatch(doSetLanguage(language)), signIn: () => dispatch(doSignIn()), requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()), diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx index c7ecf9475..e4ff246cf 100644 --- a/ui/component/app/view.jsx +++ b/ui/component/app/view.jsx @@ -61,6 +61,7 @@ type Props = { }, fetchAccessToken: () => void, fetchChannelListMine: () => void, + fetchCollectionListMine: () => void, signIn: () => void, requestDownloadUpgrade: () => void, onSignedIn: () => void, @@ -94,6 +95,7 @@ function App(props: Props) { user, fetchAccessToken, fetchChannelListMine, + fetchCollectionListMine, signIn, autoUpdateDownloaded, isUpgradeAvailable, @@ -233,8 +235,9 @@ function App(props: Props) { // @if TARGET='app' fetchChannelListMine(); // This is fetched after a user is signed in on web + fetchCollectionListMine(); // @endif - }, [appRef, fetchAccessToken, fetchChannelListMine]); + }, [appRef, fetchAccessToken, fetchChannelListMine, fetchCollectionListMine]); useEffect(() => { // $FlowFixMe diff --git a/ui/component/autoplayCountdown/index.js b/ui/component/autoplayCountdown/index.js index 68fdcf135..19134e904 100644 --- a/ui/component/autoplayCountdown/index.js +++ b/ui/component/autoplayCountdown/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { makeSelectClaimForUri, SETTINGS } from 'lbry-redux'; +import { makeSelectClaimForUri, SETTINGS, COLLECTIONS_CONSTS, makeSelectNextUrlForCollectionAndUrl } from 'lbry-redux'; import { withRouter } from 'react-router'; import { makeSelectIsPlayerFloating, makeSelectNextUnplayedRecommended } from 'redux/selectors/content'; import { makeSelectClientSetting } from 'redux/selectors/settings'; @@ -8,11 +8,24 @@ import AutoplayCountdown from './view'; import { selectModal } from 'redux/selectors/app'; /* -AutoplayCountdown does not fetch it's own next content to play, it relies on being rendered. This is dumb but I'm just the guy who noticed + AutoplayCountdown does not fetch it's own next content to play, it relies on being rendered. + This is dumb but I'm just the guy who noticed -kj */ const select = (state, props) => { - const nextRecommendedUri = makeSelectNextUnplayedRecommended(props.uri)(state); + const { location } = props; + const { search } = location; + const urlParams = new URLSearchParams(search); + const collectionId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID); + + let nextRecommendedUri; + if (collectionId) { + nextRecommendedUri = makeSelectNextUrlForCollectionAndUrl(collectionId, props.uri)(state); + } else { + nextRecommendedUri = makeSelectNextUnplayedRecommended(props.uri)(state); + } + return { + collectionId, nextRecommendedUri, nextRecommendedClaim: makeSelectClaimForUri(nextRecommendedUri)(state), isFloating: makeSelectIsPlayerFloating(props.location)(state), diff --git a/ui/component/autoplayCountdown/view.jsx b/ui/component/autoplayCountdown/view.jsx index 587520c1b..5b8df4bd3 100644 --- a/ui/component/autoplayCountdown/view.jsx +++ b/ui/component/autoplayCountdown/view.jsx @@ -6,18 +6,19 @@ import I18nMessage from 'component/i18nMessage'; import { formatLbryUrlForWeb } from 'util/url'; import { withRouter } from 'react-router'; import debounce from 'util/debounce'; - +import { COLLECTIONS_CONSTS } from 'lbry-redux'; const DEBOUNCE_SCROLL_HANDLER_MS = 150; const CLASSNAME_AUTOPLAY_COUNTDOWN = 'autoplay-countdown'; type Props = { - history: { push: string => void }, + history: { push: (string) => void }, nextRecommendedClaim: ?StreamClaim, nextRecommendedUri: string, isFloating: boolean, doSetPlayingUri: ({ uri: ?string }) => void, - doPlayUri: string => void, + doPlayUri: (string) => void, modal: { id: string, modalProps: {} }, + collectionId?: string, }; function AutoplayCountdown(props: Props) { @@ -29,6 +30,7 @@ function AutoplayCountdown(props: Props) { isFloating, history: { push }, modal, + collectionId, } = props; const nextTitle = nextRecommendedClaim && nextRecommendedClaim.value && nextRecommendedClaim.value.title; @@ -44,6 +46,11 @@ function AutoplayCountdown(props: Props) { let navigateUrl; if (nextTitle) { navigateUrl = formatLbryUrlForWeb(nextRecommendedUri); + if (collectionId) { + const collectionParams = new URLSearchParams(); + collectionParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, collectionId); + navigateUrl = navigateUrl + `?` + collectionParams.toString(); + } } const doNavigate = useCallback(() => { @@ -71,7 +78,7 @@ function AutoplayCountdown(props: Props) { // Ensure correct 'setTimerPaused' on initial render. setTimerPaused(shouldPauseAutoplay()); - const handleScroll = debounce(e => { + const handleScroll = debounce((e) => { setTimerPaused(shouldPauseAutoplay()); }, DEBOUNCE_SCROLL_HANDLER_MS); diff --git a/ui/component/channelContent/view.jsx b/ui/component/channelContent/view.jsx index 10494a9a8..109804984 100644 --- a/ui/component/channelContent/view.jsx +++ b/ui/component/channelContent/view.jsx @@ -32,6 +32,7 @@ type Props = { tileLayout: boolean, viewHiddenChannels: boolean, doResolveUris: (Array, boolean) => void, + claimType: string, }; function ChannelContent(props: Props) { @@ -49,6 +50,7 @@ function ChannelContent(props: Props) { tileLayout, viewHiddenChannels, doResolveUris, + claimType, } = props; const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0; const [searchQuery, setSearchQuery] = React.useState(''); @@ -58,6 +60,7 @@ function ChannelContent(props: Props) { } = useHistory(); const url = `${pathname}${search}`; const claimId = claim && claim.claim_id; + const showFilters = !claimType || claimType === 'stream'; function handleInputChange(e) { const { value } = e.target; @@ -134,25 +137,30 @@ function ChannelContent(props: Props) { } meta={ -
{}} className="wunderbar--inline"> - - - + showFilters && ( +
{}} className="wunderbar--inline"> + + + + ) } isChannel channelIsMine={channelIsMine} diff --git a/ui/component/claimAbandonButton/view.jsx b/ui/component/claimAbandonButton/view.jsx index 469771495..b7ce4291d 100644 --- a/ui/component/claimAbandonButton/view.jsx +++ b/ui/component/claimAbandonButton/view.jsx @@ -6,13 +6,22 @@ import Button from 'component/button'; type Props = { doOpenModal: (string, {}) => void, - claim: StreamClaim, - abandonActionCallback: any => void, + claim: Claim, + abandonActionCallback: (any) => void, iconSize: number, }; export default function ClaimAbandonButton(props: Props) { const { doOpenModal, claim, abandonActionCallback } = props; + const { value_type } = claim || {}; + let buttonLabel; + if (value_type === 'channel') { + buttonLabel = __('Delete Channel'); + } else if (value_type === 'collection') { + buttonLabel = __('Delete List'); + } else if (value_type === 'stream') { + buttonLabel = __('Delete Publish'); + } function abandonClaim() { doOpenModal(MODALS.CONFIRM_CLAIM_REVOKE, { claim: claim, cb: abandonActionCallback }); @@ -21,7 +30,7 @@ export default function ClaimAbandonButton(props: Props) { return (