Merge branch 'master' into accessibility

This commit is contained in:
Baltazar Gomez 2021-07-21 20:59:12 -05:00 committed by GitHub
commit c983af927f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 401 additions and 295 deletions

View file

@ -1,36 +1,72 @@
####### .env.default #########
# Copy this file to .env to make modifications
# Base config
MATOMO_URL=https://analytics.lbry.com/
MATOMO_ID=4
WEBPACK_WEB_PORT=9090
WEBPACK_ELECTRON_PORT=9091
WEB_SERVER_PORT=1337
## APIS
LBRY_WEB_API=https://api.na-backend.odysee.com
LBRY_WEB_STREAMING_API=https://cdn.lbryplayer.xyz
LBRY_WEB_BUFFER_API=https://collector-service.api.lbry.tv/api/v1/events/video
COMMENT_SERVER_API=https://comments.lbry.com/api/v2
THUMBNAIL_CDN_URL=https://image-processor.vanwanet.com/optimize/
WELCOME_VERSION=1.0
# Custom Site info
DOMAIN=lbry.tv
URL=https://lbry.tv
THUMBNAIL_CDN_URL=https://image-processor.vanwanet.com/optimize/
# STRIPE
STRIPE_PUBLIC_KEY='pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
# Analytics
MATOMO_URL=https://analytics.lbry.com/
MATOMO_ID=4
# OG
OG_TITLE_SUFFIX=| lbry.tv
OG_HOMEPAGE_TITLE=lbry.tv
OG_IMAGE_URL=
SITE_CANONICAL_URL=https://lbry.tv
# UI
## Custom Site info
DOMAIN=lbry.tv
URL=https://lbry.tv
SITE_TITLE=lbry.tv
SITE_NAME=lbry.tv
SITE_DESCRIPTION=Meet LBRY, an open, free, and community-controlled content wonderland.
SITE_HELP_EMAIL=help@lbry.com
LOGO_TITLE=lbry.tv
SIMPLE_SITE=false
SHOW_ADS=true
## IMAGE ASSETS
YRBL_HAPPY_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a
YRBL_SAD_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
#FAVICON=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
#LOGIN_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/login/b671946e911c66c5fa7233afb35de2badd9eceb8/0e1d81
#LOGO=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
#LOGO_TEXT_LIGHT=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
#LOGO_TEXT_DARK=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
#AVATAR_DEFAULT=
# LOCALE
DEFAULT_LANGUAGE=en
## CUSTOM SETTINGS
# Additional settings for below are found in ui/constants/settings and are for
# preventing user settings from applying to custom sites without overwriting them.
# UNSYNCED_SETTINGS='theme dark_mode_times automatic_dark_mode_enabled'
## LINKED CONTENT WHITELIST
KNOWN_APP_DOMAINS=lbry.tv,lbry.lat,odysee.com
## CUSTOM CONTENT
# If the following is true, copy custom/homepage.example.js to custom/homepage.js and modify
CUSTOM_HOMEPAGE=false
# Add channels to auto-follow on first run
AUTO_FOLLOW_CHANNELS=lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a
## FEATURES AND LIMITS
SIMPLE_SITE=false
ENABLE_COMMENT_REACTIONS=true
ENABLE_FILE_REACTIONS=false
@ -41,53 +77,26 @@ CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS=4
CHANNEL_STAKED_LEVEL_LIVESTREAM=5
WEB_PUBLISH_SIZE_LIMIT_GB=4
LOADING_BAR_COLOR=#2bbb90
SHOW_ADS=true
# OG
OG_TITLE_SUFFIX=| lbry.tv
OG_HOMEPAGE_TITLE=lbry.tv
OG_IMAGE_URL=
SITE_CANONICAL_URL=https://lbry.tv
# LOCALE
DEFAULT_LANGUAGE=en
# Custom Settings
# Additional settings for below are found in ui/constants/settings and are for
# preventing user settings from applying to custom sites without overwriting them.
# UNSYNCED_SETTINGS='theme dark_mode_times automatic_dark_mode_enabled'
KNOWN_APP_DOMAINS=lbry.tv,lbry.lat,odysee.com
# Custom Content
# If the following is true, copy custom/homepage.example.js to custom/homepage.js and modify
CUSTOM_HOMEPAGE=false
# Add channels to auto-follow on first run
AUTO_FOLLOW_CHANNELS=lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a
# Add up to 2 sidebar links:
# PINNED_URI_1=@Lbrylatam#2/Integracionesporseguridad#4
# PINNED_LABEL_1=LBRY LATAM
# PINNED_URI_2=$/discover?t=lbrytvpaidbeta&fee_amount=>0&claim_type=stream&channel_ids=5af39f818f668d8c00943c9326c5201c4fe3c423,cda9c4e92f19d6fe0764524a2012056e06ca2055,760da3ba3dd85830a843beaaed543a89b7a367e7,40c36948f0da072dcba3e4833e90f71e16de78be,e8f68563d242f6ac9784dcbc41dd86c28a9391d6,7236fc5d2783ea7314d9076ae6c8a250e3992d1a,cf7792c2a37d0d76aaaff84aff0b99a8c791429d,8316ac90764fedf3147799b7b81a6575a9cc398e,8627af93c1a1219150f06b698f4b33e6ed2f1c1e,8972a1bd06de5186e5e89292b05aac8aaa817791,c5b0b17838df2f6c31162f64d55f60f34ae8bfc6,f576d5dba905fc179de880c3fe3eb3281ea74f59,97dd77c93c9603cbb2583f3589f7f5a6c92baa43,f399d873e0c37cf24de9569b5f22bbb30a5c6709,dba870d0620d41b2b9a152c961e0c06cf875ccfc,ca1fd651c9d14bf2e5088bb2aa0146ee7aeb2ae0,50ad846a4b1543b847bf3fdafb7b45f6b2f5844c,e09ff5abe9fb44dd0dd0576894a6db60a6211603,7b6f7517f6b816827d076fa0eaad550aa315a4e7,2068452c41d8da3bd68961335da0072a99258a1a,5da63df97c8255ae94a88940695b8471657dd5a1,3645cf2f5d0bdac0523f945be1c3ff60758f7845,4da85b12244839d6368b9290f1619ff9514ab2a8,4ad942982e43326c7700b1b6443049b3cfd82161,55304f219244abf82f684f759cc0c7769242f3b4,8f42e5b592bb7f7a03f4a94a86a41b1236bb099f,e2a014d885a48f5be2dc6409610996337312facb,c18996ca488753f714d36d4654715927c1d7f9c2,ebc4214424cfa683a7046e1f794fea1e44788d84,06b6d6d6a893fb589ec2ded948f5122856921ed5,07e4546674268fc0222b2ca22d31d0549dc217ee,060940e41973d4f7f16d72a2733138e931c35f41,f8d6eccd887c9cebd36b1d42aa349279b7f5c3ed,68098b8426f967b8d04cc566348b5c128823219e,2bfe6cdb24a21bdc1b76fb7c416edd50e9e85945,1f9bb08bfa2259629f4aaa9ed40f97e9a41b6fa1,2f20148495612946675fe1c8ea99171e4d950b81,bc6938fa1e09e840056c2e831abf9664f397c472,2a6194792beac5130641e932b5ac6e5a99b5ca4f,185ba2bd547a5e4a77d29fe6c1484f47db5e058f,29cc7f6081268eaa5b3f2946e0cd0b952a94812c,49389450b1241f5d8f4c8c4271a3eb56bba33965,ffdc62ac2f7549398d3aca9d2119e83d80d588d5,d7a4d2808074b0c55d6b239f69d90e7a4930f943,d58aa4a0b2f6c2504c3abce8de3f1afb71800acc,77ae23dc7eb8a75609881d4548a79e4935a89d37,f79bce8a60fbece671f6265adc39f6469f3b9b8c,051995fdf0af634e4911704057a551e9392e62b1
# PINNED_LABEL_2=Paid Beta
# Stripe
STRIPE_PUBLIC_KEY='pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
# SIMPLE_SITE REPLACEMENTS
## SIMPLE_SITE REPLACEMENTS
ENABLE_MATURE=true
ENABLE_UI_NOTIFICATIONS=false
#ENABLE_LINK_TO_APP=true
#FORCE_ANALYTICS=true
#ENABLE_ADVANCED_FILTER=true
#ENABLE_PAID_CONTENT=true
#USE_FOOTER=true
#USE_DISCOVER_WHITELIST=false
#ENABLE_WILD_WEST=false
#FULL_SIDE_LINKS=true
#SHOW_TAGS_INTRO=false
# SEARCH TYPES
#VIDEO_ENABLED=true
#AUDIO_ENABLED=true
#POSTS_ENABLED=true
#IMAGES_ENABLED=true
#FILES_ENABLED=true
#MODELS_ENABLED=true
#ENABLE_LINK_TO_APP=true
#FORCE_ANALYTiCS=true
#ENABLE_ADVACNED_FILTER=true
#ENABLE_PAID_CONTENT=true
#USE_FOOTER=true
#USE_DISCOVER_WHITELIST=false
#ENABLE_WILD_WEST=false
#FULL_SIDE_LINKS=true
SHOW_TAGS_INTRO=true

View file

@ -141,6 +141,7 @@ function ClaimListDiscover(props: Props) {
const { search } = location;
const [page, setPage] = React.useState(1);
const [forceRefresh, setForceRefresh] = React.useState();
const [finalUris, setFinalUris] = React.useState([]);
const isLargeScreen = useIsLargeScreen();
const [orderParamEntry, setOrderParamEntry] = usePersistedState(`entry-${location.pathname}`, CS.ORDER_BY_TRENDING);
const [orderParamUser, setOrderParamUser] = usePersistedState(`orderUser-${location.pathname}`, CS.ORDER_BY_TRENDING);
@ -176,7 +177,7 @@ function ClaimListDiscover(props: Props) {
const durationParam = urlParams.get(CS.DURATION_KEY) || null;
const channelIdsInUrl = urlParams.get(CS.CHANNEL_IDS_KEY);
const channelIdsParam = channelIdsInUrl ? channelIdsInUrl.split(',') : channelIds;
const feeAmountParam = urlParams.get('fee_amount') || feeAmount || SIMPLE_SITE ? CS.FEE_AMOUNT_ONLY_FREE : undefined;
const feeAmountParam = urlParams.get('fee_amount') || feeAmount;
const originalPageSize = pageSize || CS.PAGE_SIZE;
const dynamicPageSize = isLargeScreen ? Math.ceil(originalPageSize * (3 / 2)) : originalPageSize;
const historyAction = history.action;
@ -360,9 +361,10 @@ function ClaimListDiscover(props: Props) {
}
const hasMatureTags = tagsParam && tagsParam.split(',').some((t) => MATURE_TAGS.includes(t));
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
let claimSearchResult = claimSearchByQuery[claimSearchCacheQuery];
const claimSearchResultLastPageReached = claimSearchByQueryLastPageReached[claimSearchCacheQuery];
const mainSearchKey = createNormalizedClaimSearchKey(options);
let claimSearchResult = claimSearchByQuery[mainSearchKey];
const claimSearchResultLastPageReached = claimSearchByQueryLastPageReached[mainSearchKey];
// uncomment to fix an item on a page
// const fixUri = 'lbry://@corbettreport#0/lbryodysee#5';
@ -380,6 +382,11 @@ function ClaimListDiscover(props: Props) {
// claimSearchResult.splice(2, 0, fixUri);
// }
const livestreamSearchKey = liveLivestreamsFirst
? createNormalizedClaimSearchKey(getLivestreamOnlyOptions(options))
: undefined;
const livestreamSearchResult = livestreamSearchKey && claimSearchByQuery[livestreamSearchKey];
const [prevOptions, setPrevOptions] = React.useState(null);
if (!isJustScrollingToNewPage(prevOptions, options)) {
@ -482,6 +489,17 @@ function ClaimListDiscover(props: Props) {
}
}
function urisEqual(prev: Array<string>, next: Array<string>) {
if (!prev || !next) {
// From 'ClaimList', "null" and "undefined" have special meaning,
// so we can't just compare array length here.
// - null = "timed out"
// - undefined = "no result".
return prev === next;
}
return prev.length === next.length && prev.every((value, index) => value === next[index]);
}
React.useEffect(() => {
if (shouldPerformSearch) {
const searchOptions = JSON.parse(optionsStringForEffect);
@ -493,6 +511,21 @@ function ClaimListDiscover(props: Props) {
}
}, [doClaimSearch, shouldPerformSearch, optionsStringForEffect, forceRefresh]);
// Resolve 'finalUri'
React.useEffect(() => {
if (uris) {
if (!urisEqual(uris, finalUris)) {
setFinalUris(uris);
}
} else {
// Wait until all queries are done before updating the uris to avoid layout shifts.
const pending = claimSearchResult === undefined || (liveLivestreamsFirst && livestreamSearchResult === undefined);
if (!pending && !urisEqual(claimSearchResult, finalUris)) {
setFinalUris(claimSearchResult);
}
}
}, [uris, claimSearchResult, finalUris, setFinalUris, liveLivestreamsFirst, livestreamSearchResult]);
const headerToUse = header || (
<ClaimListHeader
channelIds={channelIds}
@ -503,7 +536,7 @@ function ClaimListDiscover(props: Props) {
claimType={claimType}
streamType={streamType}
defaultStreamType={defaultStreamType}
feeAmount={SIMPLE_SITE ? undefined : feeAmount} // ENABLE_PAID_CONTENT_DISCOVER or something
feeAmount={feeAmount} // ENABLE_PAID_CONTENT_DISCOVER or something
orderBy={orderBy}
defaultOrderBy={defaultOrderBy}
hideAdvancedFilter={hideAdvancedFilter}
@ -529,9 +562,9 @@ function ClaimListDiscover(props: Props) {
)}
<ClaimList
tileLayout
id={claimSearchCacheQuery}
id={mainSearchKey}
loading={loading}
uris={uris || claimSearchResult}
uris={finalUris}
onScrollBottom={handleScrollBottom}
page={page}
pageSize={dynamicPageSize}
@ -563,10 +596,10 @@ function ClaimListDiscover(props: Props) {
</div>
)}
<ClaimList
id={claimSearchCacheQuery}
id={mainSearchKey}
type={type}
loading={loading}
uris={uris || claimSearchResult}
uris={finalUris}
onScrollBottom={handleScrollBottom}
page={page}
pageSize={dynamicPageSize}

View file

@ -5,7 +5,6 @@ import {
makeSelectFileInfoForUri,
doPrepareEdit,
makeSelectCollectionForIdHasClaimUrl,
makeSelectNameForCollectionId,
makeSelectCollectionIsMine,
COLLECTIONS_CONSTS,
makeSelectEditedCollectionForId,
@ -34,20 +33,28 @@ import fs from 'fs';
const select = (state, props) => {
const claim = makeSelectClaimForUri(props.uri, false)(state);
const permanentUri = claim && claim.permanent_url;
const repostedClaim = claim && claim.reposted_claim;
const contentClaim = repostedClaim || claim;
const contentSigningChannel = contentClaim && contentClaim.signing_channel;
const contentPermanentUri = contentClaim && contentClaim.permanent_url;
const contentChannelUri = (contentSigningChannel && contentSigningChannel.permanent_url) || contentPermanentUri;
return {
claim,
repostedClaim,
contentClaim,
contentSigningChannel,
contentChannelUri,
claimIsMine: makeSelectSigningIsMine(props.uri)(state),
hasClaimInWatchLater: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.WATCH_LATER_ID, permanentUri)(state),
hasClaimInCustom: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.FAVORITES_ID, permanentUri)(state),
channelIsMuted: makeSelectChannelIsMuted(props.uri)(state),
channelIsBlocked: makeSelectChannelIsBlocked(props.uri)(state),
hasClaimInWatchLater: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.WATCH_LATER_ID, contentPermanentUri)(state),
hasClaimInCustom: makeSelectCollectionForIdHasClaimUrl(COLLECTIONS_CONSTS.FAVORITES_ID, contentPermanentUri)(state),
channelIsMuted: makeSelectChannelIsMuted(contentChannelUri)(state),
channelIsBlocked: makeSelectChannelIsBlocked(contentChannelUri)(state),
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
isSubscribed: makeSelectIsSubscribed(props.channelUri, true)(state),
isSubscribed: makeSelectIsSubscribed(contentChannelUri, true)(state),
channelIsAdminBlocked: makeSelectChannelIsAdminBlocked(props.uri)(state),
isAdmin: selectHasAdminChannel(state),
claimInCollection: makeSelectCollectionForIdHasClaimUrl(props.collectionId, permanentUri)(state),
collectionName: makeSelectNameForCollectionId(props.collectionId)(state),
claimInCollection: makeSelectCollectionForIdHasClaimUrl(props.collectionId, contentPermanentUri)(state),
isMyCollection: makeSelectCollectionIsMine(props.collectionId)(state),
editedCollection: makeSelectEditedCollectionForId(props.collectionId)(state),
isAuthenticated: Boolean(selectUserVerifiedEmail(state)),
@ -71,7 +78,8 @@ const perform = (dispatch) => ({
doChannelUnmute: (channelUri) => dispatch(doChannelUnmute(channelUri)),
doCommentModBlock: (channelUri) => dispatch(doCommentModBlock(channelUri)),
doCommentModUnBlock: (channelUri) => dispatch(doCommentModUnBlock(channelUri)),
doCommentModBlockAsAdmin: (commenterUri, blockerId) => dispatch(doCommentModBlockAsAdmin(commenterUri, blockerId)),
doCommentModBlockAsAdmin: (commenterUri, blockerId) =>
dispatch(doCommentModBlockAsAdmin(commenterUri, blockerId)),
doCommentModUnBlockAsAdmin: (commenterUri, blockerId) =>
dispatch(doCommentModUnBlockAsAdmin(commenterUri, blockerId)),
doChannelSubscribe: (subscription) => dispatch(doChannelSubscribe(subscription)),

View file

@ -23,8 +23,11 @@ type SubscriptionArgs = {
type Props = {
uri: string,
channelUri: string,
claim: ?Claim,
repostedClaim: ?Claim,
contentClaim: ?Claim,
contentSigningChannel: ?Claim,
contentChannelUri: string,
openModal: (id: string, {}) => void,
inline?: boolean,
channelIsMuted: boolean,
@ -37,12 +40,10 @@ type Props = {
doCommentModUnBlock: (string) => void,
doCommentModBlockAsAdmin: (string, string) => void,
doCommentModUnBlockAsAdmin: (string, string) => void,
isRepost: boolean,
doCollectionEdit: (string, any) => void,
hasClaimInWatchLater: boolean,
hasClaimInCustom: boolean,
claimInCollection: boolean,
collectionName?: string,
collectionId: string,
isMyCollection: boolean,
doToast: ({ message: string, isError?: boolean }) => void,
@ -60,8 +61,11 @@ type Props = {
function ClaimMenuList(props: Props) {
const {
uri,
channelUri,
claim,
repostedClaim,
contentClaim,
contentSigningChannel,
contentChannelUri,
openModal,
inline = false,
doChannelMute,
@ -72,14 +76,12 @@ function ClaimMenuList(props: Props) {
isAdmin,
doCommentModBlock,
doCommentModUnBlock,
isRepost,
doCommentModBlockAsAdmin,
doCommentModUnBlockAsAdmin,
doCollectionEdit,
hasClaimInWatchLater,
hasClaimInCustom,
collectionId,
collectionName,
isMyCollection,
doToast,
claimIsMine,
@ -92,15 +94,16 @@ function ClaimMenuList(props: Props) {
editedCollection,
isAuthenticated,
} = props;
const repostedContent = claim && claim.reposted_claim;
const contentClaim = repostedContent || claim;
const incognitoClaim = channelUri && !channelUri.includes('@');
const signingChannel = claim && (claim.signing_channel || claim);
const permanentUrl = String(channelUri);
const isChannel = !incognitoClaim && signingChannel === claim;
const incognitoClaim = contentChannelUri && !contentChannelUri.includes('@');
const isChannel = !incognitoClaim && !contentSigningChannel;
const { channelName } = parseURI(contentChannelUri);
const showDelete = claimIsMine || (fileInfo && (fileInfo.written_bytes > 0 || fileInfo.blobs_completed > 0));
const subscriptionLabel = isSubscribed ? __('Unfollow') : __('Follow');
const subscriptionLabel = __('%action%' + '%user%', {
action: isSubscribed ? __('Unfollow') : __('Follow'),
user: repostedClaim ? __(' @' + channelName) : '',
});
const lastCollectionName = 'Favorites';
const lastCollectionId = COLLECTIONS_CONSTS.FAVORITES_ID;
const { push, replace } = useHistory();
if (!claim) {
@ -121,36 +124,46 @@ function ClaimMenuList(props: Props) {
// $FlowFixMe
(contentClaim.value.stream_type === 'audio' || contentClaim.value.stream_type === 'video');
function handleAdd(source, name, collectionId) {
doToast({
message: source ? __('Item removed from %name%', { name }) : __('Item added to %name%', { name }),
});
doCollectionEdit(collectionId, {
claims: [contentClaim],
remove: source,
type: 'playlist',
});
}
function handleFollow() {
const { channelName } = parseURI(permanentUrl);
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
subscriptionHandler({
channelName: '@' + channelName,
uri: permanentUrl,
uri: contentChannelUri,
notificationsDisabled: true,
});
}
function handleToggleMute() {
if (channelIsMuted) {
doChannelUnmute(channelUri);
doChannelUnmute(contentChannelUri);
} else {
doChannelMute(channelUri);
doChannelMute(contentChannelUri);
}
}
function handleToggleBlock() {
if (channelIsBlocked) {
doCommentModUnBlock(channelUri);
doCommentModUnBlock(contentChannelUri);
} else {
doCommentModBlock(channelUri);
doCommentModBlock(contentChannelUri);
}
}
function handleEdit() {
if (!isChannel) {
const signingChannelName = signingChannel && signingChannel.name;
const signingChannelName = contentSigningChannel && contentSigningChannel.name;
const uriObject: { streamName: string, streamClaimId: string, channelName?: string } = {
streamName: claim.name,
@ -170,10 +183,10 @@ function ClaimMenuList(props: Props) {
}
function handleDelete() {
if (!isRepost && !isChannel) {
openModal(MODALS.CONFIRM_FILE_REMOVE, { uri });
if (!repostedClaim && !isChannel) {
openModal(MODALS.CONFIRM_FILE_REMOVE, { uri, doGoBack: false });
} else {
openModal(MODALS.CONFIRM_CLAIM_REVOKE, { claim, cb: !isRepost && (() => replace(`/$/${PAGES.CHANNELS}`)) });
openModal(MODALS.CONFIRM_CLAIM_REVOKE, { claim, cb: isChannel && (() => replace(`/$/${PAGES.CHANNELS}`)) });
}
}
@ -183,9 +196,9 @@ function ClaimMenuList(props: Props) {
function handleToggleAdminBlock() {
if (channelIsAdminBlocked) {
doCommentModUnBlockAsAdmin(channelUri, '');
doCommentModUnBlockAsAdmin(contentChannelUri, '');
} else {
doCommentModBlockAsAdmin(channelUri, '');
doCommentModBlockAsAdmin(contentChannelUri, '');
}
}
@ -210,7 +223,7 @@ function ClaimMenuList(props: Props) {
function handleReportContent() {
// $FlowFixMe
push(`/$/${PAGES.REPORT_CONTENT}?claimId=${(repostedContent && repostedContent.claim_id) || claim.claim_id}`);
push(`/$/${PAGES.REPORT_CONTENT}?claimId=${contentClaim && contentClaim.claim_id}`);
}
return (
@ -228,101 +241,79 @@ function ClaimMenuList(props: Props) {
{(!IS_WEB || (IS_WEB && isAuthenticated)) && (
<>
<>
{/* WATCH LATER */}
{isPlayable && !collectionId && (
<MenuItem
className="comment__menu-option"
onSelect={() => {
doToast({
message: hasClaimInWatchLater
? __('Item removed from Watch Later')
: __('Item added to Watch Later'),
});
doCollectionEdit(COLLECTIONS_CONSTS.WATCH_LATER_ID, {
claims: [contentClaim],
remove: hasClaimInWatchLater,
type: 'playlist',
});
}}
>
<div className="menu__link">
<Icon aria-hidden icon={hasClaimInWatchLater ? ICONS.DELETE : ICONS.TIME} />
{hasClaimInWatchLater ? __('In Watch Later') : __('Watch Later')}
</div>
</MenuItem>
)}
{/* CUSTOM LIST */}
{isPlayable && !collectionId && (
<MenuItem
className="comment__menu-option"
onSelect={() => {
doToast({
message: hasClaimInCustom
? __('Item removed from %lastCollectionName%', { lastCollectionName })
: __('Item added to %lastCollectionName%', { lastCollectionName }),
});
doCollectionEdit(COLLECTIONS_CONSTS.FAVORITES_ID, {
claims: [contentClaim],
remove: hasClaimInCustom,
type: 'playlist',
});
}}
>
<div className="menu__link">
<Icon aria-hidden icon={hasClaimInCustom ? ICONS.DELETE : ICONS.STAR} />
{hasClaimInCustom
? __('In %lastCollectionName%', { lastCollectionName })
: __(`${lastCollectionName}`)}
</div>
</MenuItem>
)}
{/* COLLECTION OPERATIONS */}
{collectionId && collectionName && isCollectionClaim && (
{collectionId && isCollectionClaim ? (
<>
{Boolean(editedCollection) && (
<MenuItem
className="comment__menu-option"
onSelect={() => push(`/$/${PAGES.LIST}/${collectionId}?view=edit`)}
>
<div className="menu__link">
<Icon aria-hidden iconColor={'red'} icon={ICONS.PUBLISH} />
{__('Publish')}
</div>
</MenuItem>
)}
<MenuItem className="comment__menu-option" onSelect={() => push(`/$/${PAGES.LIST}/${collectionId}`)}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.VIEW} />
{__('View List')}
</div>
</MenuItem>
<MenuItem
className="comment__menu-option"
onSelect={() => openModal(MODALS.COLLECTION_DELETE, { collectionId })}
>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.DELETE} />
{__('Delete List')}
</div>
</MenuItem>
{isMyCollection && (
<>
<MenuItem
className="comment__menu-option"
onSelect={() => push(`/$/${PAGES.LIST}/${collectionId}?view=edit`)}
>
<div className="menu__link">
<Icon aria-hidden iconColor={'red'} icon={ICONS.PUBLISH} />
{editedCollection ? __('Publish') : __('Edit List')}
</div>
</MenuItem>
<MenuItem
className="comment__menu-option"
onSelect={() => openModal(MODALS.COLLECTION_DELETE, { collectionId })}
>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.DELETE} />
{__('Delete List')}
</div>
</MenuItem>
</>
)}
</>
)}
{/* CURRENTLY ONLY SUPPORT PLAYLISTS FOR PLAYABLE; LATER DIFFERENT TYPES */}
{isPlayable && (
<MenuItem
className="comment__menu-option"
onSelect={() => openModal(MODALS.COLLECTION_ADD, { uri, type: 'playlist' })}
>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.STACK} />
{__('Add to Lists')}
</div>
</MenuItem>
) : (
isPlayable && (
<>
{/* WATCH LATER */}
<MenuItem
className="comment__menu-option"
onSelect={() => handleAdd(hasClaimInWatchLater, 'Watch Later', COLLECTIONS_CONSTS.WATCH_LATER_ID)}
>
<div className="menu__link">
<Icon aria-hidden icon={hasClaimInWatchLater ? ICONS.DELETE : ICONS.TIME} />
{hasClaimInWatchLater ? __('In Watch Later') : __('Watch Later')}
</div>
</MenuItem>
{/* CUSTOM LIST */}
<MenuItem
className="comment__menu-option"
onSelect={() => handleAdd(hasClaimInCustom, lastCollectionName, lastCollectionId)}
>
<div className="menu__link">
<Icon aria-hidden icon={hasClaimInCustom ? ICONS.DELETE : ICONS.STAR} />
{hasClaimInCustom ? __(`In ${lastCollectionName}`) : __(`${lastCollectionName}`)}
</div>
</MenuItem>
{/* CURRENTLY ONLY SUPPORT PLAYLISTS FOR PLAYABLE; LATER DIFFERENT TYPES */}
<MenuItem
className="comment__menu-option"
onSelect={() => openModal(MODALS.COLLECTION_ADD, { uri, type: 'playlist' })}
>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.STACK} />
{__('Add to Lists')}
</div>
</MenuItem>
<hr className="menu__separator" />
</>
)
)}
</>
{!isChannelPage && (
<>
<hr className="menu__separator" />
<MenuItem className="comment__menu-option" onSelect={handleSupport}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.LBC} />
@ -332,7 +323,7 @@ function ClaimMenuList(props: Props) {
</>
)}
{!incognitoClaim && !isRepost && !claimIsMine && !isChannelPage && (
{!incognitoClaim && !claimIsMine && !isChannelPage && (
<>
<hr className="menu__separator" />
<MenuItem className="comment__menu-option" onSelect={handleFollow}>
@ -343,11 +334,11 @@ function ClaimMenuList(props: Props) {
</MenuItem>
</>
)}
{!isMyCollection && (
<>
{(!claimIsMine || channelIsBlocked) && channelUri ? (
!incognitoClaim &&
!isRepost && (
{(!claimIsMine || channelIsBlocked) && contentChannelUri ? (
!incognitoClaim && (
<>
<hr className="menu__separator" />
<MenuItem className="comment__menu-option" onSelect={handleToggleBlock}>
@ -376,7 +367,7 @@ function ClaimMenuList(props: Props) {
)
) : (
<>
{!isChannelPage && !isRepost && (
{!isChannelPage && !repostedClaim && (
<MenuItem className="comment__menu-option" onSelect={handleEdit}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.EDIT} />
@ -384,7 +375,6 @@ function ClaimMenuList(props: Props) {
</div>
</MenuItem>
)}
{showDelete && (
<MenuItem className="comment__menu-option" onSelect={handleDelete}>
<div className="menu__link">
@ -412,7 +402,7 @@ function ClaimMenuList(props: Props) {
<MenuItem className="comment__menu-option" onSelect={handleCopyLink}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.COPY_LINK} />
<Icon aria-hidden icon={ICONS.SHARE} />
{__('Copy Link')}
</div>
</MenuItem>

View file

@ -150,12 +150,14 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
collectionUris,
disableNavigation,
} = props;
const isRepost = claim && claim.repost_channel_url;
const isCollection = claim && claim.value_type === 'collection';
const collectionClaimId = isCollection && claim && claim.claim_id;
const listId = collectionId || collectionClaimId;
const WrapperElement = wrapperElement || 'li';
const shouldFetch =
claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta) && !pending);
const abandoned = !isResolvingUri && !claim;
const isMyCollection = collectionId && (isCollectionMine || collectionId.includes('-'));
const isMyCollection = listId && (isCollectionMine || listId.includes('-'));
const shouldHideActions = hideActions || isMyCollection || type === 'small' || type === 'tooltip';
const canonicalUrl = claim && claim.canonical_url;
const lastCollectionIndex = collectionUris ? collectionUris.length - 1 : 0;
@ -177,7 +179,6 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
claim.value.stream_type &&
// $FlowFixMe
(claim.value.stream_type === 'audio' || claim.value.stream_type === 'video');
const isCollection = claim && claim.value_type === 'collection';
const isChannelUri = isValid ? parseURI(uri).isChannel : false;
const signingChannel = claim && claim.signing_channel;
@ -208,12 +209,11 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
}
let navigateUrl = formatLbryUrlForWeb((claim && claim.canonical_url) || uri || '/');
if (collectionId) {
if (listId) {
const collectionParams = new URLSearchParams();
collectionParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, collectionId);
collectionParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, listId);
navigateUrl = navigateUrl + `?` + collectionParams.toString();
}
const channelUri = !isChannelUri ? signingChannel && signingChannel.permanent_url : claim && claim.permanent_url;
const navLinkProps = {
to: navigateUrl,
onClick: (e) => e.stopPropagation(),
@ -398,7 +398,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
{!pending && (
<>
{renderActions && claim && renderActions(claim)}
{Boolean(isMyCollection && collectionId) && (
{Boolean(isMyCollection && listId) && (
<>
<div className="collection-preview__edit-buttons">
<div className="collection-preview__edit-group">
@ -412,7 +412,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
e.stopPropagation();
if (editCollection) {
// $FlowFixMe
editCollection(collectionId, {
editCollection(listId, {
order: { from: collectionIndex, to: Number(collectionIndex) - 1 },
});
}
@ -428,7 +428,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
e.stopPropagation();
if (editCollection) {
// $FlowFixMe
editCollection(collectionId, {
editCollection(listId, {
order: { from: collectionIndex, to: Number(collectionIndex + 1) },
});
}
@ -443,7 +443,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
e.preventDefault();
e.stopPropagation();
// $FlowFixMe
if (editCollection) editCollection(collectionId, { claims: [claim], remove: true });
if (editCollection) editCollection(listId, { claims: [claim], remove: true });
}}
/>
</div>
@ -485,7 +485,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
</div>
</div>
{!hideMenu && (
<ClaimMenuList uri={uri} collectionId={collectionId} channelUri={channelUri} isRepost={isRepost} />
<ClaimMenuList uri={uri} collectionId={listId} />
)}
</>
</WrapperElement>

View file

@ -106,11 +106,10 @@ function ClaimPreviewTile(props: Props) {
onClick: (e) => e.stopPropagation(),
};
let isChannel;
let isValid = false;
if (uri) {
try {
({ isChannel } = parseURI(uri));
parseURI(uri);
isValid = true;
} catch (e) {
isValid = false;
@ -118,6 +117,7 @@ function ClaimPreviewTile(props: Props) {
}
const signingChannel = claim && claim.signing_channel;
const isChannel = claim && claim.value_type === 'channel';
const channelUri = !isChannel ? signingChannel && signingChannel.permanent_url : claim && claim.permanent_url;
const channelTitle = signingChannel && (signingChannel.value.title || signingChannel.name);
@ -258,7 +258,7 @@ function ClaimPreviewTile(props: Props) {
)}
</h2>
</NavLink>
<ClaimMenuList uri={uri} collectionId={listId} channelUri={channelUri} isRepost={isRepost} />
<ClaimMenuList uri={uri} collectionId={listId} channelUri={channelUri} />
</div>
<div>
<div className="claim-tile__info">

View file

@ -29,6 +29,7 @@ type Props = {
myChannels: ?Array<ChannelClaim>,
doToast: ({ message: string }) => void,
clearPlayingUri: () => void,
hideRepost?: boolean,
isLivestreamClaim: boolean,
reactionsDisabled: boolean,
download: (string) => void,
@ -48,6 +49,7 @@ function FileActions(props: Props) {
myChannels,
clearPlayingUri,
doToast,
hideRepost,
isLivestreamClaim,
reactionsDisabled,
download,
@ -113,23 +115,26 @@ function FileActions(props: Props) {
const lhsSection = (
<>
{ENABLE_FILE_REACTIONS && !reactionsDisabled && <FileReactions uri={uri} />}
{ENABLE_FILE_REACTIONS && !reactionsDisabled && <FileReactions uri={uri} livestream={isLivestreamClaim} />}
<ClaimSupportButton uri={uri} fileAction />
<ClaimCollectionAddButton uri={uri} fileAction />
<Button
className="button--file-action"
icon={ICONS.REPOST}
label={
claim.meta.reposted > 1 ? __(`%repost_total% Reposts`, { repost_total: claim.meta.reposted }) : __('Repost')
}
description={__('Repost')}
requiresAuth={IS_WEB}
onClick={handleRepostClick}
/>
{!hideRepost && (
<Button
button="alt"
className="button--file-action"
icon={ICONS.REPOST}
label={
claim.meta.reposted > 1 ? __(`%repost_total% Reposts`, { repost_total: claim.meta.reposted }) : __('Repost')
}
description={__('Repost')}
requiresAuth={IS_WEB}
onClick={handleRepostClick}
/>
)}
<Button
className="button--file-action"
icon={ICONS.SHARE}
label={__('Share')}
label={isMobile ? undefined : __('Share')}
title={__('Share')}
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, webShareable, collectionId })}
/>

View file

@ -55,6 +55,9 @@ export default function LivestreamComments(props: Props) {
const commentsLength = comments && comments.length;
const commentsToDisplay = viewMode === VIEW_MODE_CHAT ? comments : superChats;
const discussionElement = document.querySelector('.livestream__comments');
const commentElement = document.querySelector('.livestream-comment');
// todo: implement comment_list --mine in SDK so redux can grab with selectCommentIsMine
function isMyComment(channelId: string) {
if (myChannels != null && channelId != null) {
@ -81,19 +84,16 @@ export default function LivestreamComments(props: Props) {
};
}, [claimId, uri, doCommentList, doSuperChatList, doCommentSocketConnect, doCommentSocketDisconnect]);
React.useEffect(() => {
const discussionElement = document.querySelector('.livestream__comments');
const commentElement = document.querySelector('.livestream-comment');
const handleScroll = React.useCallback(() => {
if (discussionElement) {
const negativeCommentHeight = commentElement && -1 * commentElement.offsetHeight;
const isAtRecent = negativeCommentHeight && discussionElement.scrollTop >= negativeCommentHeight;
function handleScroll() {
if (discussionElement) {
const negativeCommentHeight = commentElement && -1 * commentElement.offsetHeight;
const isAtRecent = negativeCommentHeight && discussionElement.scrollTop >= negativeCommentHeight;
setScrollBottom(isAtRecent);
}
setScrollBottom(isAtRecent);
}
}, [commentElement, discussionElement]);
React.useEffect(() => {
if (discussionElement) {
discussionElement.addEventListener('scroll', handleScroll);
@ -113,15 +113,17 @@ export default function LivestreamComments(props: Props) {
return () => discussionElement.removeEventListener('scroll', handleScroll);
}
}, [commentsLength, performedInitialScroll, setPerformedInitialScroll, setScrollBottom]);
}, [commentsLength, discussionElement, handleScroll, performedInitialScroll, setPerformedInitialScroll]);
if (!claim) {
return null;
}
function scrollBack() {
const element = document.querySelector('.livestream__comments');
if (element) element.scrollTop = 0;
if (discussionElement) {
discussionElement.scrollTop = 0;
setScrollBottom(true);
}
}
return (
@ -209,7 +211,7 @@ export default function LivestreamComments(props: Props) {
button="alt"
className="livestream__comments-scroll__down"
label={__('Recent Comments')}
onClick={() => scrollBack()}
onClick={scrollBack}
/>
)}

View file

@ -89,7 +89,9 @@ function OptimizedImage(props: Props) {
} else {
setOptimizedSrc(src);
}
}, []); // eslint-disable-line react-hooks/exhaustive-deps
// We only want to run this on (1) initial mount and (2) 'src' change. Nothing else.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [src]);
if (!src) {
return null;

View file

@ -512,7 +512,7 @@ function PublishFile(props: Props) {
{fileSelectSource === SOURCE_UPLOAD && showFileUpload && (
<>
<FileSelector
label={SIMPLE_SITE ? __('Video/audio file') : __('File')}
label={__('File')}
disabled={disabled}
currentPath={currentFile}
onFileChosen={handleFileChange}

View file

@ -305,6 +305,11 @@ function SideNavigation(props: Props) {
<li className="navigation-link">
<Button label={__('FAQ')} href="https://odysee.com/@OdyseeHelp:b" />
</li>
{SIMPLE_SITE && ( // GUIDELINES_URL?
<li className="navigation-link">
<Button label={__('Community Guidelines')} href="https://odysee.com/@OdyseeHelp:b/Community-Guidelines:c" />
</li>
)}
<li className="navigation-link">
<Button label={__('Support --[used in footer; general help/support]--')} href="https://lbry.com/support" />
</li>

View file

@ -12,7 +12,7 @@ import LbcSymbol from 'component/common/lbc-symbol';
type Props = {
errorMessage: ?string,
isPending: boolean,
verifyUserIdentity: string => void,
verifyUserIdentity: (string) => void,
verifyPhone: () => void,
fetchUser: () => void,
skipLink?: string,
@ -74,7 +74,7 @@ class UserVerify extends React.PureComponent<Props> {
icon={ICONS.PHONE}
title={__('Verify phone number')}
subtitle={__(
'You will receive an SMS text message confirming your phone number is valid. Does not work for Canada and possibly other regions.'
'You will receive an SMS text message confirming your phone number is valid. May not be available in all regions.'
)}
actions={
<Fragment>

View file

@ -1,6 +1,6 @@
// @flow
import React, { useEffect, useRef, useState } from 'react';
import { SIMPLE_SITE } from 'config';
// import { SIMPLE_SITE } from 'config';
import Button from 'component/button';
import * as ICONS from 'constants/icons';
import classnames from 'classnames';
@ -13,7 +13,7 @@ import hlsQualitySelector from './plugins/videojs-hls-quality-selector/plugin';
import recsys from './plugins/videojs-recsys/plugin';
import qualityLevels from 'videojs-contrib-quality-levels';
import isUserTyping from 'util/detect-typing';
import './plugins/videojs-aniview/plugin';
// import './plugins/videojs-aniview/plugin';
const isDev = process.env.NODE_ENV !== 'production';
@ -192,7 +192,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
adUrl,
claimId,
userId,
allowPreRoll,
// allowPreRoll,
} = props;
const [reload, setReload] = useState('initial');
@ -586,9 +586,9 @@ export default React.memo<Props>(function VideoJs(props: Props) {
// This must be initialized earlier than everything else
// otherwise a race condition occurs if we place this in the onReady call back
// allow if isDev because otherwise you'll never see ads when basing to master
if ((allowPreRoll && SIMPLE_SITE) || isDev) {
vjs.aniview();
}
// if ((allowPreRoll && SIMPLE_SITE) || isDev) {
// vjs.aniview();
// }
// fixes #3498 (https://github.com/lbryio/lbry-desktop/issues/3498)
// summary: on firefox the focus would stick to the fullscreen button which caused buggy behavior with spacebar
@ -663,18 +663,19 @@ export default React.memo<Props>(function VideoJs(props: Props) {
}, [source, reload]);
// Load IMA3 SDK for aniview
useEffect(() => {
const script = document.createElement('script');
script.src = `https://imasdk.googleapis.com/js/sdkloader/ima3.js`;
script.async = true;
// $FlowFixMe
document.body.appendChild(script);
return () => {
// $FlowFixMe
document.body.removeChild(script);
};
});
// disabled for now
// useEffect(() => {
// const script = document.createElement('script');
// script.src = `https://imasdk.googleapis.com/js/sdkloader/ima3.js`;
// script.async = true;
// // $FlowFixMe
// document.body.appendChild(script);
//
// return () => {
// // $FlowFixMe
// document.body.removeChild(script);
// };
// });
return (
// $FlowFixMe

View file

@ -2,15 +2,16 @@ import { connect } from 'react-redux';
import { doDeleteFileAndMaybeGoBack } from 'redux/actions/file';
import {
makeSelectTitleForUri,
makeSelectClaimIsMine,
doResolveUri,
makeSelectClaimForUri,
makeSelectIsAbandoningClaimForUri,
} from 'lbry-redux';
import { doHideModal } from 'redux/actions/app';
import ModalRemoveFile from './view';
import { makeSelectSigningIsMine } from 'redux/selectors/content';
const select = (state, props) => ({
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
claimIsMine: makeSelectSigningIsMine(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
claim: makeSelectClaimForUri(props.uri)(state),
isAbandoning: makeSelectIsAbandoningClaimForUri(props.uri)(state),
@ -18,8 +19,9 @@ const select = (state, props) => ({
const perform = dispatch => ({
closeModal: () => dispatch(doHideModal()),
deleteFile: (uri, deleteFromComputer, abandonClaim) => {
dispatch(doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim));
doResolveUri: (uri) => dispatch(doResolveUri(uri)),
deleteFile: (uri, deleteFromComputer, abandonClaim, doGoBack) => {
dispatch(doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim, doGoBack));
},
});

View file

@ -12,8 +12,10 @@ type Props = {
uri: string,
claim: StreamClaim,
claimIsMine: boolean,
doResolveUri: (string) => void,
closeModal: () => void,
deleteFile: (string, boolean, boolean) => void,
deleteFile: (string, boolean, boolean, boolean) => void,
doGoBack: boolean,
title: string,
fileInfo?: {
outpoint: ?string,
@ -22,10 +24,16 @@ type Props = {
};
function ModalRemoveFile(props: Props) {
const { uri, claimIsMine, closeModal, deleteFile, title, claim, isAbandoning } = props;
const { uri, claimIsMine, doResolveUri, closeModal, deleteFile, doGoBack = true, title, claim, isAbandoning } = props;
const [deleteChecked, setDeleteChecked] = usePersistedState('modal-remove-file:delete', true);
const [abandonChecked, setAbandonChecked] = usePersistedState('modal-remove-file:abandon', true);
React.useEffect(() => {
if (uri) {
doResolveUri(uri);
}
}, [uri, doResolveUri]);
return (
<Modal isOpen contentLabel={__('Confirm File Remove')} type="card" onAborted={closeModal}>
<Card
@ -52,9 +60,7 @@ function ModalRemoveFile(props: Props) {
<FormField
name="claim_abandon"
label={
<I18nMessage
tokens={{ lbc: <LbcSymbol prefix={__('reclaim %amount%', { amount: claim.amount })} /> }}
>
<I18nMessage tokens={{ lbc: <LbcSymbol postfix={claim.amount} /> }}>
Remove from blockchain (%lbc%)
</I18nMessage>
}
@ -87,7 +93,7 @@ function ModalRemoveFile(props: Props) {
button="primary"
label={isAbandoning ? __('Removing...') : __('OK')}
disabled={isAbandoning || !(deleteChecked || abandonChecked)}
onClick={() => deleteFile(uri, deleteChecked, claimIsMine ? abandonChecked : false)}
onClick={() => deleteFile(uri, deleteChecked, claimIsMine ? abandonChecked : false, doGoBack)}
/>
<Button button="link" label={__('Cancel')} onClick={closeModal} />
</div>

View file

@ -218,7 +218,7 @@ function ChannelPage(props: Props) {
{!(isBlocked || isMuted) && <ClaimSupportButton uri={uri} />}
{!(isBlocked || isMuted) && (!channelIsBlackListed || isSubscribed) && <SubscribeButton uri={permanentUrl} />}
{/* TODO: add channel collections <ClaimCollectionAddButton uri={uri} fileAction /> */}
<ClaimMenuList uri={claim.permanent_url} channelUri={claim.permanent_url} inline isChannelPage />
<ClaimMenuList uri={claim.permanent_url} inline isChannelPage />
</div>
{cover && <img className={classnames('channel-cover__custom')} src={PlaceholderTx} />}
{cover && <OptimizedImage className={classnames('channel-cover__custom')} src={cover} objectFit="cover" />}

View file

@ -8,7 +8,7 @@ import ClaimTilesDiscover from 'component/claimTilesDiscover';
import ClaimListDiscover from 'component/claimListDiscover';
import * as CS from 'constants/claim_search';
import { toCapitalCase } from 'util/string';
import { CUSTOM_HOMEPAGE } from 'config';
import { CUSTOM_HOMEPAGE, SIMPLE_SITE } from 'config';
const MORE_CHANNELS_ANCHOR = 'MoreChannels';
@ -49,16 +49,6 @@ function ChannelsFollowingDiscover(props: Props) {
},
});
rowData.push({
title: 'Latest From @lbrycast',
link: `/@lbrycast:4`,
options: {
orderBy: ['release_time'],
pageSize: 8,
channelIds: ['4c29f8b013adea4d5cca1861fb2161d5089613ea'],
},
});
rowData.push({
title: 'Trending Channels',
link: `/$/${PAGES.DISCOVER}?claim_type=channel`,
@ -105,29 +95,32 @@ function ChannelsFollowingDiscover(props: Props) {
return (
<Page>
{rowDataWithGenericOptions.map(({ title, link, help, options = {} }) => (
<div key={title} className="claim-grid__wrapper">
<h1 className="section__actions">
{link ? (
<Button
className="claim-grid__title"
button="link"
navigate={link}
iconRight={ICONS.ARROW_RIGHT}
label={__(title)}
/>
) : (
<span className="claim-grid__title">{__(title)}</span>
)}
{help}
</h1>
{!SIMPLE_SITE &&
rowDataWithGenericOptions.map(({ title, link, help, options = {} }) => (
<div key={title} className="claim-grid__wrapper">
<h1 className="section__actions">
{link ? (
<Button
className="claim-grid__title"
button="link"
navigate={link}
iconRight={ICONS.ARROW_RIGHT}
label={__(title)}
/>
) : (
<span className="claim-grid__title">{__(title)}</span>
)}
{help}
</h1>
<ClaimTilesDiscover {...options} />
</div>
))}
<h1 id={MORE_CHANNELS_ANCHOR} className="claim-grid__title">
{__('More Channels')}
</h1>
<ClaimTilesDiscover {...options} />
</div>
))}
{!SIMPLE_SITE && (
<h1 id={MORE_CHANNELS_ANCHOR} className="claim-grid__title">
{__('More Channels')}
</h1>
)}
<ClaimListDiscover
defaultOrderBy={CS.ORDER_BY_TRENDING}
defaultFreshness={CS.FRESH_ALL}

View file

@ -24,6 +24,8 @@ import { makeSelectNotificationForCommentId } from 'redux/selectors/notification
import { selectActiveChannelClaim } from 'redux/selectors/app';
import { toHex } from 'util/hex';
import Comments from 'comments';
import { selectPrefsReady } from 'redux/selectors/sync';
import { doAlertWaitingForSync } from 'redux/actions/app';
const isDev = process.env.NODE_ENV !== 'production';
@ -726,8 +728,22 @@ function doCommentModToggleBlock(
) {
return async (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const ready = selectPrefsReady(state);
let blockerChannelClaims = selectMyChannelClaims(state);
if (!ready) {
return dispatch(doAlertWaitingForSync());
}
if (!blockerChannelClaims) {
return dispatch(
doToast({
message: __('Create a channel to change this setting.'),
isError: false,
})
);
}
if (blockerIds.length === 0) {
// Specific blockers not provided, so find one based on block-level.
switch (blockLevel) {

View file

@ -53,7 +53,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim, cb) {
};
}
export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim) {
export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim, doGoBack) {
return (dispatch, getState) => {
const state = getState();
const playingUri = selectPlayingUri(state);
@ -70,7 +70,9 @@ export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim
doDeleteFile(outpoint || claimOutpoint, deleteFromComputer, abandonClaim, (abandonState) => {
if (abandonState === ABANDON_STATES.DONE) {
if (abandonClaim) {
dispatch(goBack());
if (doGoBack) {
dispatch(goBack());
}
dispatch(doHideModal());
}
}

View file

@ -25,7 +25,9 @@ async function redirectMiddleware(ctx, next) {
request: { url },
} = ctx;
if (STATIC_ASSET_PATHS.includes(url) || (url.startsWith('/public/ui-') && url.endsWith('.js'))) {
const HASHED_JS_REGEX = /^\/public\/.*[a-fA-F0-9]{12}\.js$/i;
if (STATIC_ASSET_PATHS.includes(url) || HASHED_JS_REGEX.test(url)) {
ctx.set('Cache-Control', `public, max-age=${SIX_MONTHS_IN_SECONDS}`);
}

View file

@ -1,3 +1,4 @@
const { generateDownloadUrl } = require('../../ui/util/web');
const { URL, SITE_NAME, LBRY_WEB_API } = require('../../config.js');
const { Lbry } = require('lbry-redux');
const Feed = require('feed').Feed;
@ -44,7 +45,9 @@ async function getClaimsFromChannel(claimId, count) {
async function getFeed(channelClaim, feedLink) {
const replaceLineFeeds = (str) => str.replace(/(?:\r\n|\r|\n)/g, '<br />');
const fmtDescription = (description) => replaceLineFeeds(description);
const sanitizeThumbsUrl = (url) => {
if (typeof url === 'string' && url.startsWith('https://')) {
return encodeURI(url).replace(/&/g, '%26');
@ -52,6 +55,29 @@ async function getFeed(channelClaim, feedLink) {
return '';
};
const getEnclosure = (claim) => {
const value = claim.value;
if (!value || !value.stream_type || !value.source || !value.source.media_type) {
return undefined;
}
switch (value.stream_type) {
case 'video':
case 'audio':
case 'image':
case 'document':
case 'software':
return {
url: encodeURI(generateDownloadUrl(claim.name, claim.claim_id)),
type: value.source.media_type,
length: value.source.size || 0, // Per spec, 0 is a valid fallback.
};
default:
return undefined;
}
};
const value = channelClaim.value;
const title = value ? value.title : channelClaim.name;
@ -78,14 +104,18 @@ async function getFeed(channelClaim, feedLink) {
const meta = c.meta;
const value = c.value;
const title = value && value.title ? value.title : c.name;
const thumbnailUrl = value && value.thumbnail ? value.thumbnail.url : '';
const thumbnailHtml = thumbnailUrl ? `<p><img src="${thumbnailUrl}" alt="thumbnail" title="${title}" /></p>` : '';
feed.addItem({
id: c.claim_id,
guid: encodeURI(URL + '/' + c.name + ':' + c.claim_id),
title: value && value.title ? value.title : c.name,
description: fmtDescription(value && value.description ? value.description : ''),
image: sanitizeThumbsUrl(value && value.thumbnail ? value.thumbnail.url : ''),
description: thumbnailHtml + fmtDescription(value && value.description ? value.description : ''),
link: encodeURI(URL + '/' + c.name + ':' + c.claim_id),
date: new Date(meta ? meta.creation_timestamp * 1000 : null),
enclosure: getEnclosure(c),
});
});