From 63ccfc13044b1060792b33dcef32c12cb204cb63 Mon Sep 17 00:00:00 2001 From: dgarrett01 Date: Sun, 27 Nov 2022 18:05:40 -0500 Subject: [PATCH] Still learning to use Git. All files should be good now. --- CHANGELOG.md | 3 + ui/component/channelForm/view.jsx | 22 ++++- ui/component/claimListHeader/view.jsx | 1 + ui/component/claimPreview/index.js | 2 + ui/component/claimPreview/view.jsx | 15 ++- ui/component/claimPreviewTile/index.js | 2 + ui/component/claimPreviewTile/view.jsx | 22 ++++- ui/component/searchOptions/view.jsx | 131 ++++++++++++++++--------- ui/scss/component/_claim-search.scss | 16 +++ 9 files changed, 160 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9366e1b00..63f788579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [0.53.8] - [2022-11-17] +### Added +- Added the ability for users to hide content they've already watched throughout the app via a checkbox ([#7645](https://github.com/lbryio/lbry-desktop/issues/7645)) + ### Fixed - Selecting a large file in publish no longer crashes ([#7736](https://github.com/lbryio/lbry-desktop/pull/7736)) - Unfollowing unpublished channels ([#7737](https://github.com/lbryio/lbry-desktop/pull/7737)) diff --git a/ui/component/channelForm/view.jsx b/ui/component/channelForm/view.jsx index 236d42ae5..fa0357cb3 100644 --- a/ui/component/channelForm/view.jsx +++ b/ui/component/channelForm/view.jsx @@ -106,6 +106,7 @@ function ChannelForm(props: Props) { const languageParam = params.languages; const primaryLanguage = Array.isArray(languageParam) && languageParam.length && languageParam[0]; const secondaryLanguage = Array.isArray(languageParam) && languageParam.length >= 2 && languageParam[1]; + const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false); const submitLabel = React.useMemo(() => { if (isClaimingInitialRewards) { return __('Claiming credits...'); @@ -238,7 +239,23 @@ function ChannelForm(props: Props) { } if ((!isUpload.thumbnail && thumbError) || (!isUpload.cover && coverError)) { errorMsg = __('Invalid %error_type%', { error_type: (thumbError && 'thumbnail') || (coverError && 'cover image') }); - } + } + + function getHideWatchedElem() { + return ( +
+ { + setHideWatched((prev) => !prev); + }} + /> +
+ ); + } React.useEffect(() => { let nameError; @@ -525,7 +542,8 @@ function ChannelForm(props: Props) {
replace(`/$/${PAGES.CHANNELS}`)} />
- )} + )} + {getHideWatchedElem()} } /> diff --git a/ui/component/claimListHeader/view.jsx b/ui/component/claimListHeader/view.jsx index 91ac5b0e8..976e6288c 100644 --- a/ui/component/claimListHeader/view.jsx +++ b/ui/component/claimListHeader/view.jsx @@ -67,6 +67,7 @@ function ClaimListHeader(props: Props) { const [orderParamUser, setOrderParamUser] = usePersistedState(`orderUser-${location.pathname}`, CS.ORDER_BY_TRENDING); const urlParams = new URLSearchParams(search); const freshnessParam = freshness || urlParams.get(CS.FRESH_KEY) || defaultFreshness; + const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false); const contentTypeParam = urlParams.get(CS.CONTENT_KEY); const streamTypeParam = streamType || (CS.FILE_TYPES.includes(contentTypeParam) && contentTypeParam) || defaultStreamType || null; diff --git a/ui/component/claimPreview/index.js b/ui/component/claimPreview/index.js index df20d0ac5..dbbded0b0 100644 --- a/ui/component/claimPreview/index.js +++ b/ui/component/claimPreview/index.js @@ -21,6 +21,7 @@ import { selectIsSubscribedForUri } from 'redux/selectors/subscriptions'; import { isClaimNsfw } from 'util/claim'; import ClaimPreview from './view'; import formatMediaDuration from 'util/formatMediaDuration'; +import { makeSelectContentWatchedPercentageForUri } from 'redux/selectors/content'; const select = (state, props) => { const claim = props.uri && selectClaimForUri(state, props.uri); @@ -46,6 +47,7 @@ const select = (state, props) => { wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state), isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state), lang: selectLanguage(state), + isWatched: makeSelectContentWatchedPercentageForUri(props.uri)(state) > 80, }; }; diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx index 9717eeec7..35d55bf37 100644 --- a/ui/component/claimPreview/view.jsx +++ b/ui/component/claimPreview/view.jsx @@ -32,6 +32,7 @@ import ClaimPreviewNoContent from './claim-preview-no-content'; import CollectionEditButtons from 'component/collectionEditButtons'; import { useIsMobile } from 'effects/use-screensize'; import AbandonedChannelPreview from 'component/abandonedChannelPreview'; +import usePersistedState from 'effects/use-persisted-state'; // preview images used on the landing page and on the channel page type Props = { @@ -82,6 +83,7 @@ type Props = { showEdit?: boolean, dragHandleProps?: any, unavailableUris?: Array, + isWatched: boolean, }; const ClaimPreview = forwardRef((props: Props, ref: any) => { @@ -141,10 +143,11 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { showEdit, dragHandleProps, unavailableUris, + isWatched, } = props; const isMobile = useIsMobile(); - + const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false); const isCollection = claim && claim.value_type === 'collection'; const collectionClaimId = isCollection && claim && claim.claim_id; const listId = collectionId || collectionClaimId; @@ -278,7 +281,15 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { } }, [isValid, uri, isResolvingUri, shouldFetch, resolveUri]); - if (shouldHide && !showNullPlaceholder) { + if (isWatched && hideWatched) { + shouldHide = true; + } + + if (shouldHide) { + return null; + } + + if (shouldHide && !showNullPlaceholder) { return null; } diff --git a/ui/component/claimPreviewTile/index.js b/ui/component/claimPreviewTile/index.js index 95b209ff8..5240f836c 100644 --- a/ui/component/claimPreviewTile/index.js +++ b/ui/component/claimPreviewTile/index.js @@ -10,6 +10,7 @@ import { doFileGet } from 'redux/actions/file'; import { doResolveUri } from 'redux/actions/claims'; import { selectViewCountForUri, selectBanStateForUri } from 'lbryinc'; import { selectShowMatureContent } from 'redux/selectors/settings'; +import { makeSelectContentWatchedPercentageForUri } from 'redux/selectors/content'; import { isClaimNsfw } from 'util/claim'; import ClaimPreviewTile from './view'; import formatMediaDuration from 'util/formatMediaDuration'; @@ -30,6 +31,7 @@ const select = (state, props) => { showMature: selectShowMatureContent(state), isMature: claim ? isClaimNsfw(claim) : false, viewCount: selectViewCountForUri(state, props.uri), + isWatched: makeSelectContentWatchedPercentageForUri(props.uri)(state) > 80, }; }; diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index aea7bd313..63cca1e49 100644 --- a/ui/component/claimPreviewTile/view.jsx +++ b/ui/component/claimPreviewTile/view.jsx @@ -20,6 +20,7 @@ import ClaimMenuList from 'component/claimMenuList'; import CollectionPreviewOverlay from 'component/collectionPreviewOverlay'; // $FlowFixMe cannot resolve ... import PlaceholderTx from 'static/img/placeholderTx.gif'; +import usePersistedState from 'effects/use-persisted-state'; type Props = { uri: string, @@ -42,6 +43,7 @@ type Props = { collectionId?: string, viewCount: string, swipeLayout: boolean, + isWatched: boolean, }; // preview image cards used in related video functionality, channel overview page and homepage @@ -67,10 +69,12 @@ function ClaimPreviewTile(props: Props) { mediaDuration, viewCount, swipeLayout = false, + isWatched, } = props; const isRepost = claim && claim.repost_channel_url; const isCollection = claim && claim.value_type === 'collection'; const isStream = claim && claim.value_type === 'stream'; + const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false); // $FlowFixMe const isPlayable = claim && @@ -124,7 +128,23 @@ function ClaimPreviewTile(props: Props) { } }, [isValid, isResolvingUri, uri, resolveUri, shouldFetch]); - let shouldHide = false; + let shouldHide = false; + + if (isMature && !showMature) { + // Unfortunately needed until this is resolved + // https://github.com/lbryio/lbry-sdk/issues/2785 + shouldHide = true; + } else { + shouldHide = + banState.blacklisted || + banState.filtered || + (!showHiddenByUser && (banState.muted || banState.blocked)) || + (isWatched && hideWatched); + } + + if (shouldHide) { + return null; + } if (isMature && !showMature) { // Unfortunately needed until this is resolved diff --git a/ui/component/searchOptions/view.jsx b/ui/component/searchOptions/view.jsx index 70aa32c5f..5a75c3c0c 100644 --- a/ui/component/searchOptions/view.jsx +++ b/ui/component/searchOptions/view.jsx @@ -61,6 +61,8 @@ const SearchOptions = (props: Props) => { delete TYPES_ADVANCED[SEARCH_OPTIONS.MEDIA_IMAGE]; } + const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false); + React.useEffect(() => { // We no longer let the user set the search results count, but the value // will be in local storage for existing users. Override that. @@ -69,7 +71,31 @@ const SearchOptions = (props: Props) => { } }, []); - function updateSearchOptions(option, value) { + function getHideWatchedElem() { + return ( +
+ { + setHideWatched((prev) => !prev); + }} + /> + +
+ ); + } + + function updateSearchOptions(option, value) { setSearchOption(option, value); if (onSearchOptionsChanged) { onSearchOptionsChanged(option); @@ -149,28 +175,28 @@ const SearchOptions = (props: Props) => { ); - const otherOptionsElem = ( - <> -
- updateSearchOptions(SEARCH_OPTIONS.EXACT, !options[SEARCH_OPTIONS.EXACT])} - label={__('Exact match')} - /> - -
- - ); + const exactMatchElem = ( + <> +
+ updateSearchOptions(SEARCH_OPTIONS.EXACT, !options[SEARCH_OPTIONS.EXACT])} + /> + +
+ + + ); const uploadDateElem = (
@@ -194,7 +220,13 @@ const SearchOptions = (props: Props) => {
); - const sortByElem = ( + const hideWatchedElem = ( +
+ {getHideWatchedElem()} +
+ ); + + const sortByElem = (
{ const uploadDateLabel = options[SEARCH_OPTIONS.CLAIM_TYPE] === SEARCH_OPTIONS.INCLUDE_CHANNELS ? __('Creation Date') : __('Upload Date'); - return ( -
-
- ); + return ( +
+
+ ); }; export default SearchOptions; diff --git a/ui/scss/component/_claim-search.scss b/ui/scss/component/_claim-search.scss index 94cc15e6b..f7ca6f0ae 100644 --- a/ui/scss/component/_claim-search.scss +++ b/ui/scss/component/_claim-search.scss @@ -27,6 +27,22 @@ } } +// UPDATE: Add style for checkbox +.claim-search__checkbox { + padding-left: 45%; + padding-top: 7%; +} + +//UPDATE: Added style for checkbox on search page +.claim-search__checkbox_searchbox { + // Placeholder +} + +// UPDATE: Add style for checkbox label +.checkbox-label { + padding-left: 10px; +} + .claim-search__dropdown { font-size: var(--font-body); background-color: var(--color-input-bg);