diff --git a/.vs/lbry-desktop/v17/.wsuo b/.vs/lbry-desktop/v17/.wsuo index 5fa85337d..af626fd28 100644 Binary files a/.vs/lbry-desktop/v17/.wsuo and b/.vs/lbry-desktop/v17/.wsuo differ diff --git a/ui/component/channelForm/view.jsx b/ui/component/channelForm/view.jsx index 236d42ae5..3c0e7c9c7 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); // UPDATE: Experimenting with hiding watched content const submitLabel = React.useMemo(() => { if (isClaimingInitialRewards) { return __('Claiming credits...'); @@ -240,7 +241,24 @@ function ChannelForm(props: Props) { errorMsg = __('Invalid %error_type%', { error_type: (thumbError && 'thumbnail') || (coverError && 'cover image') }); } - React.useEffect(() => { + // UPDATE: Add "Hide Watched" to channel pages + function getHideWatchedElem() { + return ( +
+ { + setHideWatched((prev) => !prev); + }} + /> +
+ ); + } + + React.useEffect(() => { let nameError; if (!name && name !== undefined) { nameError = __('A name is required for your url'); @@ -525,7 +543,8 @@ function ChannelForm(props: Props) {
replace(`/$/${PAGES.CHANNELS}`)} />
- )} + )} + {getHideWatchedElem()} } /> diff --git a/ui/component/claimListDiscover/view.jsx b/ui/component/claimListDiscover/view.jsx index f15a20483..496a02019 100644 --- a/ui/component/claimListDiscover/view.jsx +++ b/ui/component/claimListDiscover/view.jsx @@ -318,25 +318,21 @@ function ClaimListDiscover(props: Props) { if (orderParam === CS.ORDER_BY_TOP && freshnessParam !== CS.FRESH_ALL) { options.release_time = `>${Math.floor(moment().subtract(1, freshnessParam).startOf('hour').unix())}`; } else if (orderParam === CS.ORDER_BY_NEW || orderParam === CS.ORDER_BY_TRENDING) { - // Warning - hack below - // If users are following more than 10 channels or tags, limit results to stuff less than a year old - // For more than 20, drop it down to 6 months - // This helps with timeout issues for users that are following a ton of stuff - // https://github.com/lbryio/lbry-sdk/issues/2420 - if ( - (options.channel_ids && options.channel_ids.length > 20) || - (options.any_tags && options.any_tags.length > 20) - ) { - options.release_time = `>${Math.floor(moment().subtract(3, CS.FRESH_MONTH).startOf('week').unix())}`; - } else if ( - (options.channel_ids && options.channel_ids.length > 10) || - (options.any_tags && options.any_tags.length > 10) - ) { - options.release_time = `>${Math.floor(moment().subtract(1, CS.FRESH_YEAR).startOf('week').unix())}`; - } else { - // Hack for at least the New page until https://github.com/lbryio/lbry-sdk/issues/2591 is fixed - options.release_time = `<${Math.floor(moment().startOf('minute').unix())}`; - } + // UPDATE: Commented out these lines of code to truly sort by oldest first... + //if ( + // (options.channel_ids && options.channel_ids.length > 20) || + // (options.any_tags && options.any_tags.length > 20) + //) { + // options.release_time = `>${Math.floor(moment().subtract(3, CS.FRESH_MONTH).startOf('week').unix())}`; + //} else if ( + // (options.channel_ids && options.channel_ids.length > 10) || + // (options.any_tags && options.any_tags.length > 10) + //) { + // options.release_time = `>${Math.floor(moment().subtract(1, CS.FRESH_YEAR).startOf('week').unix())}`; + //} else { + // // Hack for at least the New page until https://github.com/lbryio/lbry-sdk/issues/2591 is fixed + // options.release_time = `<${Math.floor(moment().startOf('minute').unix())}`; + //} } } diff --git a/ui/component/claimListHeader/view.jsx b/ui/component/claimListHeader/view.jsx index 91ac5b0e8..e12bf7cb4 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); // UPDATE: Experimenting with hiding watched content const contentTypeParam = urlParams.get(CS.CONTENT_KEY); const streamTypeParam = streamType || (CS.FILE_TYPES.includes(contentTypeParam) && contentTypeParam) || defaultStreamType || null; @@ -97,7 +98,24 @@ function ClaimListHeader(props: Props) { const shouldHighlight = searchInLanguage ? languageParam !== languageSetting && languageParam !== null - : languageParam !== CS.LANGUAGES_ALL && languageParam !== null; + : languageParam !== CS.LANGUAGES_ALL && languageParam !== null; + + // UPDATE: Experimenting with hiding watched content + // Adding a Hide Watched checkbox to the main menu + function getHideWatchedElem() { + return ( +
+ { + setHideWatched((prev) => !prev); + }} + /> +
+ ); + } React.useEffect(() => { if (action !== 'POP' && isFiltered()) { @@ -481,10 +499,10 @@ function ClaimListHeader(props: Props) { ); })} - - - )} + + + )} {channelIdsInUrl && (
@@ -495,10 +513,14 @@ function ClaimListHeader(props: Props) { onClick={handleAdvancedReset} />
- )} + )} +
+ + {getHideWatchedElem()} +
- )} + )} {hasMatureTags && hiddenNsfwMessage} diff --git a/ui/component/claimPreview/index.js b/ui/component/claimPreview/index.js index df20d0ac5..8a9db3a88 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'; // UPDATE: Added mSCWPFU (Watched 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, // UPDATE: Added isWatched, getting percent watched }; }; diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx index 9717eeec7..06cd514e7 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'; // UPDATE: usePersistedState is required for watched content // 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, // UPDATE: Declare isWatched variable }; const ClaimPreview = forwardRef((props: Props, ref: any) => { @@ -141,10 +143,11 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { showEdit, dragHandleProps, unavailableUris, + isWatched, // UPDATE: Variables to use in the ClaimPreviewTile } = props; const isMobile = useIsMobile(); - + const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false); //UPDATE: Use hideWatched const isCollection = claim && claim.value_type === 'collection'; const collectionClaimId = isCollection && claim && claim.claim_id; const listId = collectionId || collectionClaimId; @@ -253,8 +256,9 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { if (customShouldHide(claim)) { shouldHide = true; } - } + } + // Weird placement warning // Make sure this happens after we figure out if this claim needs to be hidden const thumbnailUrl = useGetThumbnail(uri, claim, streamingUrl, getFile, shouldHide); @@ -272,15 +276,25 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { } } - useEffect(() => { + useEffect(() => { if (isValid && !isResolvingUri && shouldFetch && uri) { resolveUri(uri); } }, [isValid, uri, isResolvingUri, shouldFetch, resolveUri]); - if (shouldHide && !showNullPlaceholder) { + // UPDATE: Hiding watched content + if (isWatched && hideWatched) { + shouldHide = true; + } + + if (shouldHide) { + return null; + } + // END OF UPDATE: + + if (shouldHide && !showNullPlaceholder) { return null; - } + } if (placeholder === 'loading' || (uri && !claim && isResolvingUri)) { return ; diff --git a/ui/component/claimPreviewTile/index.js b/ui/component/claimPreviewTile/index.js index 95b209ff8..bfcd29f2d 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'; // UPDATE: Added percentage watched element 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, // UPDATE: Get isWatched view percentage and only show < 80% }; }; diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index aea7bd313..29f14d357 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'; // UPDATE: usePersistedState is required for watched content type Props = { uri: string, @@ -42,6 +43,7 @@ type Props = { collectionId?: string, viewCount: string, swipeLayout: boolean, + isWatched: boolean, // UPDATE: Declare isWatched variable }; // 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, // UPDATE: Variables to use in the ClaimPreviewTile } = 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); //UPDATE: Use hideWatched // $FlowFixMe const isPlayable = claim && @@ -126,18 +130,23 @@ function ClaimPreviewTile(props: Props) { 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)); - } + // UPDATE: Hiding watched content + 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 (shouldHide) { + return null; + } + // END OF UPDATE: const isChannelPage = location.pathname.startsWith('/@'); diff --git a/ui/component/searchOptions/view.jsx b/ui/component/searchOptions/view.jsx index 70aa32c5f..a2fe769b1 100644 --- a/ui/component/searchOptions/view.jsx +++ b/ui/component/searchOptions/view.jsx @@ -6,6 +6,7 @@ import { Form, FormField } from 'component/common/form'; import Button from 'component/button'; import Icon from 'component/common/icon'; import classnames from 'classnames'; +import usePersistedState from 'effects/use-persisted-state'; const CLAIM_TYPES = { [SEARCH_OPTIONS.INCLUDE_FILES]: 'Files', @@ -59,9 +60,11 @@ const SearchOptions = (props: Props) => { if (simple) { delete TYPES_ADVANCED[SEARCH_OPTIONS.MEDIA_APPLICATION]; delete TYPES_ADVANCED[SEARCH_OPTIONS.MEDIA_IMAGE]; - } + } - React.useEffect(() => { + const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false); // UPDATE: Experimenting with hiding watched content + + 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. if (options[SEARCH_OPTIONS.RESULT_COUNT] !== SEARCH_PAGE_SIZE) { @@ -69,7 +72,32 @@ const SearchOptions = (props: Props) => { } }, []); - function updateSearchOptions(option, value) { + // UPDATE: Adding Hide Watched Content checkbox to search + function getHideWatchedElem() { + return ( +
+ { + setHideWatched((prev) => !prev); + }} + /> + +
+ ); + } + + function updateSearchOptions(option, value) { setSearchOption(option, value); if (onSearchOptionsChanged) { onSearchOptionsChanged(option); @@ -149,7 +177,8 @@ const SearchOptions = (props: Props) => { ); - const otherOptionsElem = ( + // UPDATE: Changed element name to exactMatchElem + const exactMatchElem = ( <>
{ name="exact-match" checked={options[SEARCH_OPTIONS.EXACT]} onChange={() => updateSearchOptions(SEARCH_OPTIONS.EXACT, !options[SEARCH_OPTIONS.EXACT])} - label={__('Exact match')} /> { customTooltipText={__( 'Find results that include all the given words in the exact order.\nThis can also be done by surrounding the search query with quotation marks (e.g. "hello world").' )} - /> -
+ /> + + ); @@ -192,7 +221,14 @@ const SearchOptions = (props: Props) => { onClick={() => updateSearchOptions(SEARCH_OPTIONS.TIME_FILTER, '')} /> - ); + ); + + // UPDATE: Declare constant for the Hide Watch Content element + const hideWatchedElem = ( +
+ {getHideWatchedElem()} +
+ ); const sortByElem = (
@@ -208,10 +244,11 @@ const SearchOptions = (props: Props) => {
); - const uploadDateLabel = + const uploadDateLabel = options[SEARCH_OPTIONS.CLAIM_TYPE] === SEARCH_OPTIONS.INCLUDE_CHANNELS ? __('Creation Date') : __('Upload Date'); - return ( + // UPDATE: Added row to table for hiding watched content in search settings + return (