Still learning to use Git. All files should be good now.

This commit is contained in:
dgarrett01 2022-11-27 18:05:40 -05:00
parent 121e22e17f
commit 63ccfc1304
9 changed files with 160 additions and 54 deletions

View file

@ -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] ## [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 ### Fixed
- Selecting a large file in publish no longer crashes ([#7736](https://github.com/lbryio/lbry-desktop/pull/7736)) - 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)) - Unfollowing unpublished channels ([#7737](https://github.com/lbryio/lbry-desktop/pull/7737))

View file

@ -106,6 +106,7 @@ function ChannelForm(props: Props) {
const languageParam = params.languages; const languageParam = params.languages;
const primaryLanguage = Array.isArray(languageParam) && languageParam.length && languageParam[0]; const primaryLanguage = Array.isArray(languageParam) && languageParam.length && languageParam[0];
const secondaryLanguage = Array.isArray(languageParam) && languageParam.length >= 2 && languageParam[1]; const secondaryLanguage = Array.isArray(languageParam) && languageParam.length >= 2 && languageParam[1];
const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false);
const submitLabel = React.useMemo(() => { const submitLabel = React.useMemo(() => {
if (isClaimingInitialRewards) { if (isClaimingInitialRewards) {
return __('Claiming credits...'); return __('Claiming credits...');
@ -238,7 +239,23 @@ function ChannelForm(props: Props) {
} }
if ((!isUpload.thumbnail && thumbError) || (!isUpload.cover && coverError)) { if ((!isUpload.thumbnail && thumbError) || (!isUpload.cover && coverError)) {
errorMsg = __('Invalid %error_type%', { error_type: (thumbError && 'thumbnail') || (coverError && 'cover image') }); errorMsg = __('Invalid %error_type%', { error_type: (thumbError && 'thumbnail') || (coverError && 'cover image') });
} }
function getHideWatchedElem() {
return (
<div className={classnames(`card claim-search__menus`)}>
<FormField
label={__('Hide Watched')}
name="hide_watched"
type="checkbox"
checked={hideWatched}
onChange={() => {
setHideWatched((prev) => !prev);
}}
/>
</div>
);
}
React.useEffect(() => { React.useEffect(() => {
let nameError; let nameError;
@ -525,7 +542,8 @@ function ChannelForm(props: Props) {
<div className="section__actions"> <div className="section__actions">
<ClaimAbandonButton uri={uri} abandonActionCallback={() => replace(`/$/${PAGES.CHANNELS}`)} /> <ClaimAbandonButton uri={uri} abandonActionCallback={() => replace(`/$/${PAGES.CHANNELS}`)} />
</div> </div>
)} )}
{getHideWatchedElem()}
</> </>
} }
/> />

View file

@ -67,6 +67,7 @@ function ClaimListHeader(props: Props) {
const [orderParamUser, setOrderParamUser] = usePersistedState(`orderUser-${location.pathname}`, CS.ORDER_BY_TRENDING); const [orderParamUser, setOrderParamUser] = usePersistedState(`orderUser-${location.pathname}`, CS.ORDER_BY_TRENDING);
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const freshnessParam = freshness || urlParams.get(CS.FRESH_KEY) || defaultFreshness; const freshnessParam = freshness || urlParams.get(CS.FRESH_KEY) || defaultFreshness;
const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false);
const contentTypeParam = urlParams.get(CS.CONTENT_KEY); const contentTypeParam = urlParams.get(CS.CONTENT_KEY);
const streamTypeParam = const streamTypeParam =
streamType || (CS.FILE_TYPES.includes(contentTypeParam) && contentTypeParam) || defaultStreamType || null; streamType || (CS.FILE_TYPES.includes(contentTypeParam) && contentTypeParam) || defaultStreamType || null;

View file

@ -21,6 +21,7 @@ import { selectIsSubscribedForUri } from 'redux/selectors/subscriptions';
import { isClaimNsfw } from 'util/claim'; import { isClaimNsfw } from 'util/claim';
import ClaimPreview from './view'; import ClaimPreview from './view';
import formatMediaDuration from 'util/formatMediaDuration'; import formatMediaDuration from 'util/formatMediaDuration';
import { makeSelectContentWatchedPercentageForUri } from 'redux/selectors/content';
const select = (state, props) => { const select = (state, props) => {
const claim = props.uri && selectClaimForUri(state, props.uri); const claim = props.uri && selectClaimForUri(state, props.uri);
@ -46,6 +47,7 @@ const select = (state, props) => {
wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state), wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state),
isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state), isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state),
lang: selectLanguage(state), lang: selectLanguage(state),
isWatched: makeSelectContentWatchedPercentageForUri(props.uri)(state) > 80,
}; };
}; };

View file

@ -32,6 +32,7 @@ import ClaimPreviewNoContent from './claim-preview-no-content';
import CollectionEditButtons from 'component/collectionEditButtons'; import CollectionEditButtons from 'component/collectionEditButtons';
import { useIsMobile } from 'effects/use-screensize'; import { useIsMobile } from 'effects/use-screensize';
import AbandonedChannelPreview from 'component/abandonedChannelPreview'; import AbandonedChannelPreview from 'component/abandonedChannelPreview';
import usePersistedState from 'effects/use-persisted-state';
// preview images used on the landing page and on the channel page // preview images used on the landing page and on the channel page
type Props = { type Props = {
@ -82,6 +83,7 @@ type Props = {
showEdit?: boolean, showEdit?: boolean,
dragHandleProps?: any, dragHandleProps?: any,
unavailableUris?: Array<string>, unavailableUris?: Array<string>,
isWatched: boolean,
}; };
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => { const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
@ -141,10 +143,11 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
showEdit, showEdit,
dragHandleProps, dragHandleProps,
unavailableUris, unavailableUris,
isWatched,
} = props; } = props;
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false);
const isCollection = claim && claim.value_type === 'collection'; const isCollection = claim && claim.value_type === 'collection';
const collectionClaimId = isCollection && claim && claim.claim_id; const collectionClaimId = isCollection && claim && claim.claim_id;
const listId = collectionId || collectionClaimId; const listId = collectionId || collectionClaimId;
@ -278,7 +281,15 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
} }
}, [isValid, uri, isResolvingUri, shouldFetch, resolveUri]); }, [isValid, uri, isResolvingUri, shouldFetch, resolveUri]);
if (shouldHide && !showNullPlaceholder) { if (isWatched && hideWatched) {
shouldHide = true;
}
if (shouldHide) {
return null;
}
if (shouldHide && !showNullPlaceholder) {
return null; return null;
} }

View file

@ -10,6 +10,7 @@ import { doFileGet } from 'redux/actions/file';
import { doResolveUri } from 'redux/actions/claims'; import { doResolveUri } from 'redux/actions/claims';
import { selectViewCountForUri, selectBanStateForUri } from 'lbryinc'; import { selectViewCountForUri, selectBanStateForUri } from 'lbryinc';
import { selectShowMatureContent } from 'redux/selectors/settings'; import { selectShowMatureContent } from 'redux/selectors/settings';
import { makeSelectContentWatchedPercentageForUri } from 'redux/selectors/content';
import { isClaimNsfw } from 'util/claim'; import { isClaimNsfw } from 'util/claim';
import ClaimPreviewTile from './view'; import ClaimPreviewTile from './view';
import formatMediaDuration from 'util/formatMediaDuration'; import formatMediaDuration from 'util/formatMediaDuration';
@ -30,6 +31,7 @@ const select = (state, props) => {
showMature: selectShowMatureContent(state), showMature: selectShowMatureContent(state),
isMature: claim ? isClaimNsfw(claim) : false, isMature: claim ? isClaimNsfw(claim) : false,
viewCount: selectViewCountForUri(state, props.uri), viewCount: selectViewCountForUri(state, props.uri),
isWatched: makeSelectContentWatchedPercentageForUri(props.uri)(state) > 80,
}; };
}; };

View file

@ -20,6 +20,7 @@ import ClaimMenuList from 'component/claimMenuList';
import CollectionPreviewOverlay from 'component/collectionPreviewOverlay'; import CollectionPreviewOverlay from 'component/collectionPreviewOverlay';
// $FlowFixMe cannot resolve ... // $FlowFixMe cannot resolve ...
import PlaceholderTx from 'static/img/placeholderTx.gif'; import PlaceholderTx from 'static/img/placeholderTx.gif';
import usePersistedState from 'effects/use-persisted-state';
type Props = { type Props = {
uri: string, uri: string,
@ -42,6 +43,7 @@ type Props = {
collectionId?: string, collectionId?: string,
viewCount: string, viewCount: string,
swipeLayout: boolean, swipeLayout: boolean,
isWatched: boolean,
}; };
// preview image cards used in related video functionality, channel overview page and homepage // preview image cards used in related video functionality, channel overview page and homepage
@ -67,10 +69,12 @@ function ClaimPreviewTile(props: Props) {
mediaDuration, mediaDuration,
viewCount, viewCount,
swipeLayout = false, swipeLayout = false,
isWatched,
} = props; } = props;
const isRepost = claim && claim.repost_channel_url; const isRepost = claim && claim.repost_channel_url;
const isCollection = claim && claim.value_type === 'collection'; const isCollection = claim && claim.value_type === 'collection';
const isStream = claim && claim.value_type === 'stream'; const isStream = claim && claim.value_type === 'stream';
const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false);
// $FlowFixMe // $FlowFixMe
const isPlayable = const isPlayable =
claim && claim &&
@ -124,7 +128,23 @@ function ClaimPreviewTile(props: Props) {
} }
}, [isValid, isResolvingUri, uri, resolveUri, shouldFetch]); }, [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) { if (isMature && !showMature) {
// Unfortunately needed until this is resolved // Unfortunately needed until this is resolved

View file

@ -61,6 +61,8 @@ const SearchOptions = (props: Props) => {
delete TYPES_ADVANCED[SEARCH_OPTIONS.MEDIA_IMAGE]; delete TYPES_ADVANCED[SEARCH_OPTIONS.MEDIA_IMAGE];
} }
const [hideWatched, setHideWatched] = usePersistedState('hideWatched', false);
React.useEffect(() => { React.useEffect(() => {
// We no longer let the user set the search results count, but the value // We no longer let the user set the search results count, but the value
// will be in local storage for existing users. Override that. // 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 (
<div className={`claim-search__checkbox_searchbox`}>
<FormField
name="hide_watched"
type="checkbox"
checked={hideWatched}
onChange={() => {
setHideWatched((prev) => !prev);
}}
/>
<Icon
className="icon--help"
icon={ICONS.HELP}
tooltip
size={16}
customTooltipText={__(
'Hide content you have already viewed from search results.'
)}
/>
</div>
);
}
function updateSearchOptions(option, value) {
setSearchOption(option, value); setSearchOption(option, value);
if (onSearchOptionsChanged) { if (onSearchOptionsChanged) {
onSearchOptionsChanged(option); onSearchOptionsChanged(option);
@ -149,28 +175,28 @@ const SearchOptions = (props: Props) => {
</> </>
); );
const otherOptionsElem = ( const exactMatchElem = (
<> <>
<div className="filter-values"> <div className="filter-values">
<FormField <FormField
type="checkbox" type="checkbox"
name="exact-match" name="exact-match"
checked={options[SEARCH_OPTIONS.EXACT]} checked={options[SEARCH_OPTIONS.EXACT]}
onChange={() => updateSearchOptions(SEARCH_OPTIONS.EXACT, !options[SEARCH_OPTIONS.EXACT])} onChange={() => updateSearchOptions(SEARCH_OPTIONS.EXACT, !options[SEARCH_OPTIONS.EXACT])}
label={__('Exact match')} />
/> <Icon
<Icon className="icon--help"
className="icon--help" icon={ICONS.HELP}
icon={ICONS.HELP} tooltip
tooltip size={16}
size={16} customTooltipText={__(
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").'
'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").' )}
)} />
/> </div>
</div>
</> </>
); );
const uploadDateElem = ( const uploadDateElem = (
<div className="filter-values"> <div className="filter-values">
@ -194,7 +220,13 @@ const SearchOptions = (props: Props) => {
</div> </div>
); );
const sortByElem = ( const hideWatchedElem = (
<div>
{getHideWatchedElem()}
</div>
);
const sortByElem = (
<div className="filter-values"> <div className="filter-values">
<FormField <FormField
type="select" type="select"
@ -211,31 +243,32 @@ const SearchOptions = (props: Props) => {
const uploadDateLabel = const uploadDateLabel =
options[SEARCH_OPTIONS.CLAIM_TYPE] === SEARCH_OPTIONS.INCLUDE_CHANNELS ? __('Creation Date') : __('Upload Date'); options[SEARCH_OPTIONS.CLAIM_TYPE] === SEARCH_OPTIONS.INCLUDE_CHANNELS ? __('Creation Date') : __('Upload Date');
return ( return (
<div> <div>
<Button <Button
button="alt" button="alt"
label={__('Filter')} label={__('Filter')}
icon={ICONS.FILTER} icon={ICONS.FILTER}
iconRight={expanded ? ICONS.UP : ICONS.DOWN} iconRight={expanded ? ICONS.UP : ICONS.DOWN}
onClick={toggleSearchExpanded} onClick={toggleSearchExpanded}
/> />
<Form <Form
className={classnames('search__options', { className={classnames('search__options', {
'search__options--expanded': expanded, 'search__options--expanded': expanded,
})} })}
> >
<table className="table table--condensed"> <table className="table table--condensed">
<tbody> <tbody>
{addRow(__('Type'), typeElem)} {addRow(__('Type'), typeElem)}
{addRow(uploadDateLabel, uploadDateElem)} {addRow(uploadDateLabel, uploadDateElem)}
{addRow(__('Sort By'), sortByElem)} {addRow(__('Sort By'), sortByElem)}
{addRow(__('Other Options'), otherOptionsElem)} {addRow(__('Exact Match'), exactMatchElem)}
</tbody> {addRow(__('Hide Watched Content'), hideWatchedElem)}
</table> </tbody>
</Form> </table>
</div> </Form>
); </div>
);
}; };
export default SearchOptions; export default SearchOptions;

View file

@ -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 { .claim-search__dropdown {
font-size: var(--font-body); font-size: var(--font-body);
background-color: var(--color-input-bg); background-color: var(--color-input-bg);