Merge branch 'master' into accessibility
This commit is contained in:
commit
74087d2b86
61 changed files with 604 additions and 350 deletions
|
@ -46,6 +46,7 @@ YRBL_SAD_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649
|
|||
#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=
|
||||
#FAVICON=
|
||||
|
||||
# LOCALE
|
||||
DEFAULT_LANGUAGE=en
|
||||
|
@ -77,6 +78,8 @@ CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS=4
|
|||
CHANNEL_STAKED_LEVEL_LIVESTREAM=5
|
||||
WEB_PUBLISH_SIZE_LIMIT_GB=4
|
||||
LOADING_BAR_COLOR=#2bbb90
|
||||
LIGHTHOUSE_DEFAULT_TYPES=audio,video,text,image,application
|
||||
|
||||
SHOW_ADS=true
|
||||
|
||||
## SIMPLE_SITE REPLACEMENTS
|
||||
|
|
|
@ -25,9 +25,10 @@ const config = {
|
|||
// LOGO
|
||||
LOGO_TITLE: process.env.LOGO_TITLE,
|
||||
FAVICON: process.env.FAVICON,
|
||||
LOGO_URL: process.env.LOGO_URL,
|
||||
LOGO_TEXT_LIGHT_URL: process.env.LOGO_TEXT_LIGHT_URL,
|
||||
LOGO_TEXT_DARK_URL: process.env.LOGO_TEXT_DARK_URL,
|
||||
LOGO: process.env.LOGO,
|
||||
LOGO_TEXT_LIGHT: process.env.LOGO_TEXT_LIGHT,
|
||||
LOGO_TEXT_DARK: process.env.LOGO_TEXT_DARK,
|
||||
AVATAR_DEFAULT: process.env.AVATAR_DEFAULT,
|
||||
// OG
|
||||
OG_TITLE_SUFFIX: process.env.OG_TITLE_SUFFIX,
|
||||
OG_HOMEPAGE_TITLE: process.env.OG_HOMEPAGE_TITLE,
|
||||
|
@ -40,6 +41,7 @@ const config = {
|
|||
DEFAULT_LANGUAGE: process.env.DEFAULT_LANGUAGE,
|
||||
AUTO_FOLLOW_CHANNELS: process.env.AUTO_FOLLOW_CHANNELS,
|
||||
UNSYNCED_SETTINGS: process.env.UNSYNCED_SETTINGS,
|
||||
AVATAR_DEFAULT: process.env.AVATAR_DEFAULT,
|
||||
|
||||
// ENABLE FEATURES
|
||||
ENABLE_COMMENT_REACTIONS: process.env.ENABLE_COMMENT_REACTIONS === 'true',
|
||||
|
@ -63,6 +65,7 @@ const config = {
|
|||
ENABLE_MATURE: process.env.ENABLE_MATURE === 'true',
|
||||
CUSTOM_HOMEPAGE: process.env.CUSTOM_HOMEPAGE === 'true',
|
||||
SHOW_TAGS_INTRO: process.env.SHOW_TAGS_INTRO === 'true',
|
||||
LIGHTHOUSE_DEFAULT_TYPES: process.env.LIGHTHOUSE_DEFAULT_TYPES,
|
||||
};
|
||||
|
||||
config.URL_LOCAL = `http://localhost:${config.WEB_SERVER_PORT}`;
|
||||
|
|
2
flow-typed/homepage.js
vendored
2
flow-typed/homepage.js
vendored
|
@ -17,7 +17,7 @@ declare type RowDataItem = {
|
|||
help?: any,
|
||||
icon?: string,
|
||||
extra?: any,
|
||||
pinUrls?: Array<string>,
|
||||
pinnedUrls?: Array<string>,
|
||||
options?: {
|
||||
channelIds?: Array<string>,
|
||||
limitClaimsPerChannel?: number,
|
||||
|
|
4
homepages/meme/index.js
Normal file
4
homepages/meme/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
text: `This is LBRY`,
|
||||
url: 'https://odysee.com/@Odysee:8?view=discussion',
|
||||
};
|
|
@ -375,6 +375,10 @@
|
|||
"Got it": "Got it",
|
||||
"View your channels": "View your channels",
|
||||
"Unfollow": "Unfollow",
|
||||
"Follow @%channelName%": "Follow @%channelName%",
|
||||
"Unfollow @%channelName%": "Unfollow @%channelName%",
|
||||
"Item removed from %name%": "Item removed from %name%",
|
||||
"Item added to %name%": "Item added to %name%",
|
||||
"These LBRY Credits remain yours. It is a deposit to reserve the name and can be undone at any time.": "These LBRY Credits remain yours. It is a deposit to reserve the name and can be undone at any time.",
|
||||
"Create channel": "Create channel",
|
||||
"Uh oh. The flux in our Retro Encabulator must be out of whack. Try refreshing to fix it.": "Uh oh. The flux in our Retro Encabulator must be out of whack. Try refreshing to fix it.",
|
||||
|
@ -390,7 +394,6 @@
|
|||
"Share on Telegram": "Share on Telegram",
|
||||
"Share via...": "Share via...",
|
||||
"View on lbry.tv": "View on lbry.tv",
|
||||
"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. Does not work for Canada and possibly other regions.",
|
||||
"Standard messaging rates apply. LBRY will not text or call you otherwise. Having trouble?": "Standard messaging rates apply. LBRY will not text or call you otherwise. Having trouble?",
|
||||
"You currently have the highest bid for this name.": "You currently have the highest bid for this name.",
|
||||
"You can generate a new address at any time, and any previous addresses will continue to work.": "You can generate a new address at any time, and any previous addresses will continue to work.",
|
||||
|
@ -623,7 +626,7 @@
|
|||
"If Sync is on, LBRY will backup your wallet and preferences. If disabled, you are responsible for keeping a backup.": "If Sync is on, LBRY will backup your wallet and preferences. If disabled, you are responsible for keeping a backup.",
|
||||
"Your update is now pending on LBRY. It will take a few minutes to appear for other users.": "Your update is now pending on LBRY. It will take a few minutes to appear for other users.",
|
||||
"Your livestream is now pending. You will be able to start shortly at the streaming dashboard.": "Your livestream is now pending. You will be able to start shortly at the streaming dashboard.",
|
||||
"Your file is now pending on LBRY. It will take a few minutes to appear for other users.": "Your file is now pending on LBRY. It will take a few minutes to appear for other users.",
|
||||
"Your content will be live shortly.": "Your content will be live shortly.",
|
||||
"Your video will appear on Odysee shortly.": "Your video will appear on Odysee shortly.",
|
||||
"Upload will continue in the background, please do not shut down immediately. Leaving the app running helps the network, thank you!": "Upload will continue in the background, please do not shut down immediately. Leaving the app running helps the network, thank you!",
|
||||
"No results for %query%": "No results for %query%",
|
||||
|
@ -740,7 +743,6 @@
|
|||
"Livestream Created": "Livestream Created",
|
||||
"Delete this file from my computer": "Delete this file from my computer",
|
||||
"Are you sure you'd like to remove %title%?": "Are you sure you'd like to remove %title%?",
|
||||
"reclaim %amount%": "reclaim %amount%",
|
||||
"Remove from blockchain (%lbc%)": "Remove from blockchain (%lbc%)",
|
||||
"Abandon on blockchain (reclaim %lbc%)": "Abandon on blockchain (reclaim %lbc%)",
|
||||
"This will increase the overall bid amount for this content, which will boost its ability to be discovered while active.": "This will increase the overall bid amount for this content, which will boost its ability to be discovered while active.",
|
||||
|
@ -859,7 +861,7 @@
|
|||
"This will permanently remove your channel. Content published under this channel will be orphaned.": "This will permanently remove your channel. Content published under this channel will be orphaned.",
|
||||
"Are you sure you'd like to remove \"%title%\"?": "Are you sure you'd like to remove \"%title%\"?",
|
||||
"You are signed into lbry.tv which automatically shares data with LBRY inc. %signout_button%.": "You are signed into lbry.tv which automatically shares data with LBRY inc. %signout_button%.",
|
||||
"LBRY works better if you find and follow a couple creators you like. You can also block channels you never want to see.": "LBRY works better if you find and follow a couple creators you like. You can also block channels you never want to see.",
|
||||
"%SITE_NAME% works better if you find and follow a couple creators you like. You can also block channels you never want to see.": "%SITE_NAME% works better if you find and follow a couple creators you like. You can also block channels you never want to see.",
|
||||
"Nice! You are currently following %followingCount% creator": "Nice! You are currently following %followingCount% creator",
|
||||
"Nice! You are currently following %followingCount% creators": "Nice! You are currently following %followingCount% creators",
|
||||
"Get Validated": "Get Validated",
|
||||
|
@ -997,7 +999,7 @@
|
|||
"You sent %amount% LBRY Credits as a tip, Mahalo!": "You sent %amount% LBRY Credits as a tip, Mahalo!",
|
||||
"You sent %amount% LBRY Credits": "You sent %amount% LBRY Credits",
|
||||
"No stats found": "No stats found",
|
||||
"Sorry about that. Try refreshing or something else.": "Sorry about that. Try refreshing or something else.",
|
||||
"Sorry about that. Contact %SITE_HELP_EMAIL% if you continue to have issues.": "Sorry about that. Contact %SITE_HELP_EMAIL% if you continue to have issues.",
|
||||
"You are not able to see this channel's stats. Make sure you are signed in with the correct email and have data sharing turned on.": "You are not able to see this channel's stats. Make sure you are signed in with the correct email and have data sharing turned on.",
|
||||
"%follower_count% followers": "%follower_count% followers",
|
||||
"Sign Up": "Sign Up",
|
||||
|
@ -1414,15 +1416,13 @@
|
|||
"Confirm Edit": "Confirm Edit",
|
||||
"Create Livestream": "Create Livestream",
|
||||
"File": "File",
|
||||
"Video/audio file": "Video/audio file",
|
||||
"Transcode": "Transcode",
|
||||
"Estimated transaction fee:": "Estimated transaction fee:",
|
||||
"Est. transaction fee:": "Est. transaction fee:",
|
||||
"Skip preview and confirmation": "Skip preview and confirmation",
|
||||
"Upload settings": "Upload settings",
|
||||
"Currently uploading": "Currently uploading",
|
||||
"Your files are currently uploading.": "Your files are currently uploading.",
|
||||
"Your file is currently uploading.": "Your file is currently uploading.",
|
||||
"Currently Uploading": "Currently Uploading",
|
||||
"Leave the app running until upload is complete": "Leave the app running until upload is complete",
|
||||
"Enable Sync": "Enable Sync",
|
||||
"Disable Sync": "Disable Sync",
|
||||
"Getting your profiles...": "Getting your profiles...",
|
||||
|
@ -1453,6 +1453,7 @@
|
|||
"Unable to comment. This channel has blocked you.": "Unable to comment. This channel has blocked you.",
|
||||
"Unable to comment. Your channel has been blocked by an admin.": "Unable to comment. Your channel has been blocked by an admin.",
|
||||
"Unable to comment. The content owner has disabled comments.": "Unable to comment. The content owner has disabled comments.",
|
||||
"Please do not spam.": "Please do not spam.",
|
||||
"Slow mode is on. Please wait up to %value% seconds before commenting again.": "Slow mode is on. Please wait up to %value% seconds before commenting again.",
|
||||
"The comment contains contents that are blocked by %author%": "The comment contains contents that are blocked by %author%",
|
||||
"Your channel is still being setup, try again in a few moments.": "Your channel is still being setup, try again in a few moments.",
|
||||
|
@ -1495,7 +1496,7 @@
|
|||
"Earn a random reward of at least %lbc% for watching anything at all.": "Earn a random reward of at least %lbc% for watching anything at all.",
|
||||
"You have already claimed this reward within the last 24 hours.": "You have already claimed this reward within the last 24 hours.",
|
||||
"Please watch some content to earn this reward.": "Please watch some content to earn this reward.",
|
||||
"You will receive an SMS text message confirming your phone number is valid.": "You will receive an SMS text message confirming your phone number is valid.",
|
||||
"You will receive an SMS text message confirming your phone number is valid. May not be available in all regions.": "You will receive an SMS text message confirming your phone number is valid. May not be available in all regions.",
|
||||
"Verified accounts are eligible to earn LBRY Credits for views, watching and reposting videos, sharing invite links etc. Verifying also helps us keep the %SITE_NAME% community safe too! %Refresh% or %Skip%.": "Verified accounts are eligible to earn LBRY Credits for views, watching and reposting videos, sharing invite links etc. Verifying also helps us keep the %SITE_NAME% community safe too! %Refresh% or %Skip%.",
|
||||
"Your First Nickel": "Your First Nickel",
|
||||
"Your First Credit": "Your First Credit",
|
||||
|
@ -1735,11 +1736,14 @@
|
|||
"Moderator Block": "Moderator Block",
|
||||
"Block this channel on behalf of %creator%": "Block this channel on behalf of %creator%",
|
||||
"creator": "creator",
|
||||
"Create a channel to change this setting.": "Create a channel to change this setting.",
|
||||
"Invalid channel URL \"%url%\"": "Invalid channel URL \"%url%\"",
|
||||
"Delegation": "Delegation",
|
||||
"Add moderator": "Add moderator",
|
||||
"Add moderators": "Add moderators",
|
||||
"Add moderator channel URL (e.g. \"@lbry#3f\")": "Add moderator channel URL (e.g. \"@lbry#3f\")",
|
||||
"Enter a @username or URL": "Enter a @username or URL",
|
||||
"examples: @channel, @channel#3, https://odysee.com/@Odysee:8, lbry://@Odysee#8": "examples: @channel, @channel#3, https://odysee.com/@Odysee:8, lbry://@Odysee#8",
|
||||
"Moderators": "Moderators",
|
||||
"Add as moderator": "Add as moderator",
|
||||
"Mute (m)": "Mute (m)",
|
||||
"Playback Rate (<, >)": "Playback Rate (<, >)",
|
||||
"Fullscreen (f)": "Fullscreen (f)",
|
||||
|
@ -1763,9 +1767,10 @@
|
|||
"Stay up to date on the latest content from your favorite creators.": "Stay up to date on the latest content from your favorite creators.",
|
||||
"Receive tutorial emails related to LBRY ": "Receive tutorial emails related to LBRY ",
|
||||
"Create A LiveStream": "Create A LiveStream",
|
||||
"This channel": "This channel",
|
||||
"%channel% has disabled chat for this stream. Enjoy the stream!": "%channel% has disabled chat for this stream. Enjoy the stream!",
|
||||
"This channel has disabled chat for this stream. Enjoy the stream!": "This channel has disabled chat for this stream. Enjoy the stream!",
|
||||
"%channel% isn't live right now, but the chat is! Check back later to watch the stream.": "%channel% isn't live right now, but the chat is! Check back later to watch the stream.",
|
||||
"This channel isn't live right now, but the chat is! Check back later to watch the stream.": "This channel isn't live right now, but the chat is! Check back later to watch the stream.",
|
||||
"Right now": "Right now",
|
||||
"%viewer_count% currently watching": "%viewer_count% currently watching",
|
||||
"Slowmode is on. You can comment again in %time% seconds.": "Slowmode is on. You can comment again in %time% seconds.",
|
||||
|
@ -1781,6 +1786,9 @@
|
|||
"Nothing here yet. %check_again%": "Nothing here yet. %check_again%",
|
||||
"Check again": "Check again",
|
||||
"Check again...": "Check again...",
|
||||
"Only visible to you": "Only visible to you",
|
||||
"People who view this link will be redirected to your livestream. Make sure to use this for sharing so your title and thumbnail are displayed properly.": "People who view this link will be redirected to your livestream. Make sure to use this for sharing so your title and thumbnail are displayed properly.",
|
||||
"View livestream": "View livestream",
|
||||
"You need to upload your livestream details before you can go live. If you already created one in this channel, it should appear soon.": "You need to upload your livestream details before you can go live. If you already created one in this channel, it should appear soon.",
|
||||
"Create A Livestream": "Create A Livestream",
|
||||
"Create a Livestream by first submitting your livestream details and waiting for approval confirmation. This can be done well in advance and will take a few minutes.": "Create a Livestream by first submitting your livestream details and waiting for approval confirmation. This can be done well in advance and will take a few minutes.",
|
||||
|
@ -1803,7 +1811,7 @@
|
|||
"Update your livestream": "Update your livestream",
|
||||
"Prepare an upcoming livestream": "Prepare an upcoming livestream",
|
||||
"Edit your post": "Edit your post",
|
||||
"Update your video": "Update your video",
|
||||
"Update your content": "Update your content",
|
||||
"Go Live on Odysee": "Go Live on Odysee",
|
||||
"You're invited to try out our new livestreaming service while in beta!": "You're invited to try out our new livestreaming service while in beta!",
|
||||
"lbry.tv is being retired in favor of %odysee% and new sign ups are disabled. Sign up on %odysee% instead": "lbry.tv is being retired in favor of %odysee% and new sign ups are disabled. Sign up on %odysee% instead",
|
||||
|
@ -1951,7 +1959,6 @@
|
|||
"Failed to process fetched data.": "Failed to process fetched data.",
|
||||
"More from %claim_name%": "More from %claim_name%",
|
||||
"Update your video/audio": "Update your video/audio",
|
||||
"Upload that unlabeled video you found behind the TV in 1991": "Upload that unlabeled video you found behind the TV in 1991",
|
||||
"Upload that unlabeled video or cassette you found behind the TV in 1991": "Upload that unlabeled video or cassette you found behind the TV in 1991",
|
||||
"Select Replay": "Select Replay",
|
||||
"Craft an epic post clearly explaining... whatever.": "Craft an epic post clearly explaining... whatever.",
|
||||
|
@ -2049,7 +2056,6 @@
|
|||
"Tip Creators": "Tip Creators",
|
||||
"Only select creators can receive tips at this time": "Only select creators can receive tips at this time",
|
||||
"The payment will be made from your saved card": "The payment will be made from your saved card",
|
||||
"A channel is required to comment on lbry.tv": "A channel is required to comment on lbry.tv",
|
||||
"Commenting...": "Commenting...",
|
||||
"Show %count% replies": "Show %count% replies",
|
||||
"Show reply": "Show reply",
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<link rel="icon" type="image/png" href="/public/favicon.png" />
|
||||
|
||||
<link rel="preload" href="/public/font/v1/300.woff" as="font" type="font/woff" crossorigin />
|
||||
<link rel="preload" href="/public/font/v1/300i.woff" as="font" type="font/woff" crossorigin />
|
||||
|
|
|
@ -12,7 +12,7 @@ import { generateInitialUrl } from 'util/url';
|
|||
import { MATOMO_ID, MATOMO_URL, LBRY_WEB_BUFFER_API } from 'config';
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const devInternalApis = process.env.LBRY_API_URL;
|
||||
const devInternalApis = process.env.LBRY_API_URL && process.env.LBRY_API_URL.includes('dev');
|
||||
|
||||
export const SHARE_INTERNAL = 'shareInternal';
|
||||
const SHARE_THIRD_PARTY = 'shareThirdParty';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import { SHOW_ADS, ENABLE_NO_SOURCE_CLAIMS, SIMPLE_SITE } from 'config';
|
||||
import { SHOW_ADS, SIMPLE_SITE } from 'config';
|
||||
import * as CS from 'constants/claim_search';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import React, { Fragment } from 'react';
|
||||
|
@ -136,7 +136,7 @@ function ChannelContent(props: Props) {
|
|||
{!channelIsMine && claimsInChannel > 0 && <HiddenNsfwClaims uri={uri} />}
|
||||
|
||||
<ClaimListDiscover
|
||||
showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS}
|
||||
hasSource
|
||||
defaultFreshness={CS.FRESH_ALL}
|
||||
showHiddenByUser={viewHiddenChannels}
|
||||
forceShowReposts
|
||||
|
|
|
@ -6,6 +6,7 @@ import Gerbil from './gerbil.png';
|
|||
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
|
||||
import ChannelStakedIndicator from 'component/channelStakedIndicator';
|
||||
import OptimizedImage from 'component/optimizedImage';
|
||||
import { AVATAR_DEFAULT } from 'config';
|
||||
|
||||
type Props = {
|
||||
thumbnail: ?string,
|
||||
|
@ -48,6 +49,7 @@ function ChannelThumbnail(props: Props) {
|
|||
const channelThumbnail = thumbnail || thumbnailPreview;
|
||||
const isGif = channelThumbnail && channelThumbnail.endsWith('gif');
|
||||
const showThumb = (!obscure && !!thumbnail) || thumbnailPreview;
|
||||
const defaultAvater = AVATAR_DEFAULT || Gerbil;
|
||||
|
||||
// Generate a random color class based on the first letter of the channel name
|
||||
const { channelName } = parseURI(uri);
|
||||
|
@ -86,7 +88,7 @@ function ChannelThumbnail(props: Props) {
|
|||
<OptimizedImage
|
||||
alt={__('Channel profile picture')}
|
||||
className="channel-thumbnail__default"
|
||||
src={!thumbError && channelThumbnail ? channelThumbnail : Gerbil}
|
||||
src={!thumbError && channelThumbnail ? channelThumbnail : defaultAvater}
|
||||
loading={noLazyLoad ? undefined : 'lazy'}
|
||||
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
|
||||
/>
|
||||
|
@ -99,7 +101,7 @@ function ChannelThumbnail(props: Props) {
|
|||
<OptimizedImage
|
||||
alt={__('Channel profile picture')}
|
||||
className="channel-thumbnail__custom"
|
||||
src={!thumbError && channelThumbnail ? channelThumbnail : Gerbil}
|
||||
src={!thumbError && channelThumbnail ? channelThumbnail : defaultAvater}
|
||||
loading={noLazyLoad ? undefined : 'lazy'}
|
||||
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
|
||||
/>
|
||||
|
|
|
@ -98,10 +98,13 @@ function ClaimMenuList(props: Props) {
|
|||
const isChannel = !incognitoClaim && !contentSigningChannel;
|
||||
const { channelName } = parseURI(contentChannelUri);
|
||||
const showDelete = claimIsMine || (fileInfo && (fileInfo.written_bytes > 0 || fileInfo.blobs_completed > 0));
|
||||
const subscriptionLabel = __('%action%' + '%user%', {
|
||||
action: isSubscribed ? __('Unfollow') : __('Follow'),
|
||||
user: repostedClaim ? __(' @' + channelName) : '',
|
||||
});
|
||||
const subscriptionLabel = repostedClaim
|
||||
? isSubscribed
|
||||
? __('Unfollow @%channelName%', { channelName })
|
||||
: __('Follow @%channelName%', { channelName })
|
||||
: isSubscribed
|
||||
? __('Unfollow')
|
||||
: __('Follow');
|
||||
const lastCollectionName = 'Favorites';
|
||||
const lastCollectionId = COLLECTIONS_CONSTS.FAVORITES_ID;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ function ClaimPreviewLoading(props: Props) {
|
|||
className={classnames('claim-preview__wrapper', {
|
||||
'claim-preview__wrapper--channel': isChannel && type !== 'inline',
|
||||
'claim-preview__wrapper--inline': type === 'inline',
|
||||
'claim-preview__wrapper--small': type === 'small',
|
||||
})}
|
||||
>
|
||||
<div className={classnames('claim-preview', { 'claim-preview--large': type === 'large' })}>
|
||||
|
|
|
@ -114,7 +114,6 @@ type Props = {
|
|||
renderProperties?: (Claim) => ?Node,
|
||||
liveLivestreamsFirst?: boolean,
|
||||
livestreamMap?: { [string]: any },
|
||||
pin?: boolean,
|
||||
pinUrls?: Array<string>,
|
||||
showNoSourceClaims?: boolean,
|
||||
};
|
||||
|
@ -147,7 +146,6 @@ function ClaimTilesDiscover(props: Props) {
|
|||
mutedUris,
|
||||
liveLivestreamsFirst,
|
||||
livestreamMap,
|
||||
pin,
|
||||
pinUrls,
|
||||
prefixUris,
|
||||
showNoSourceClaims,
|
||||
|
@ -291,9 +289,9 @@ function ClaimTilesDiscover(props: Props) {
|
|||
};
|
||||
|
||||
const modifiedUris = uris ? uris.slice() : [];
|
||||
const fixUris = pinUrls || ['lbry://@AlisonMorrow#6/LBRY#8'];
|
||||
const fixUris = pinUrls || [];
|
||||
|
||||
if (pin && modifiedUris && modifiedUris.length > 2 && window.location.pathname === '/') {
|
||||
if (pinUrls && modifiedUris && modifiedUris.length > 2 && window.location.pathname === '/') {
|
||||
fixUris.forEach((fixUri) => {
|
||||
if (modifiedUris.indexOf(fixUri) !== -1) {
|
||||
modifiedUris.splice(modifiedUris.indexOf(fixUri), 1);
|
||||
|
@ -308,7 +306,13 @@ function ClaimTilesDiscover(props: Props) {
|
|||
<ul className="claim-grid">
|
||||
{modifiedUris && modifiedUris.length
|
||||
? modifiedUris.map((uri, index) => (
|
||||
<ClaimPreviewTile key={uri} uri={uri} properties={renderProperties} live={resolveLive(index)} />
|
||||
<ClaimPreviewTile
|
||||
showNoSourceClaims={hasNoSource || showNoSourceClaims}
|
||||
key={uri}
|
||||
uri={uri}
|
||||
properties={renderProperties}
|
||||
live={resolveLive(index)}
|
||||
/>
|
||||
))
|
||||
: new Array(pageSize)
|
||||
.fill(1)
|
||||
|
|
|
@ -42,7 +42,9 @@ type Props = {
|
|||
isReply: boolean,
|
||||
activeChannel: string,
|
||||
activeChannelClaim: ?ChannelClaim,
|
||||
bottom: boolean,
|
||||
livestream?: boolean,
|
||||
embed?: boolean,
|
||||
toast: (string) => void,
|
||||
claimIsMine: boolean,
|
||||
sendTip: ({}, (any) => void, (any) => void) => void,
|
||||
|
@ -63,7 +65,9 @@ export function CommentCreate(props: Props) {
|
|||
isReply,
|
||||
parentId,
|
||||
activeChannelClaim,
|
||||
bottom,
|
||||
livestream,
|
||||
embed,
|
||||
claimIsMine,
|
||||
sendTip,
|
||||
doToast,
|
||||
|
@ -106,7 +110,7 @@ export function CommentCreate(props: Props) {
|
|||
|
||||
function altEnterListener(e: SyntheticKeyboardEvent<*>) {
|
||||
const KEYCODE_ENTER = 13;
|
||||
if ((e.ctrlKey || e.metaKey) && e.keyCode === KEYCODE_ENTER) {
|
||||
if ((livestream || e.ctrlKey || e.metaKey) && e.keyCode === KEYCODE_ENTER) {
|
||||
e.preventDefault();
|
||||
buttonref.current.click();
|
||||
}
|
||||
|
@ -147,8 +151,6 @@ export function CommentCreate(props: Props) {
|
|||
const activeChannelName = activeChannelClaim && activeChannelClaim.name;
|
||||
const activeChannelId = activeChannelClaim && activeChannelClaim.claim_id;
|
||||
|
||||
console.log(activeChannelClaim);
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
if (activeTab === TAB_LBC) {
|
||||
|
@ -280,6 +282,11 @@ export function CommentCreate(props: Props) {
|
|||
<div
|
||||
role="button"
|
||||
onClick={() => {
|
||||
if (embed) {
|
||||
window.open(`https://odysee.com/$/${PAGES.AUTH}?redirect=/$/${PAGES.LIVESTREAM}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const pathPlusRedirect = `/$/${PAGES.CHANNEL_NEW}?redirect=${pathname}`;
|
||||
if (livestream) {
|
||||
window.open(pathPlusRedirect);
|
||||
|
@ -344,6 +351,7 @@ export function CommentCreate(props: Props) {
|
|||
className={classnames('comment__create', {
|
||||
'comment__create--reply': isReply,
|
||||
'comment__create--nested-reply': isNested,
|
||||
'comment__create--bottom': bottom,
|
||||
})}
|
||||
>
|
||||
<FormField
|
||||
|
|
|
@ -2323,4 +2323,12 @@ export const icons = {
|
|||
<path d="M12.729 1.2l3.346 6.629 6.44.638a.805.805 0 01.5 1.374l-5.3 5.253 1.965 7.138a.813.813 0 01-1.151.935L12 19.934l-6.52 3.229a.813.813 0 01-1.151-.935l1.965-7.138L.99 9.837a.805.805 0 01.5-1.374l6.44-.638L11.271 1.2a.819.819 0 011.458 0z" />
|
||||
</g>
|
||||
),
|
||||
[ICONS.MUSIC]: buildIcon(
|
||||
<g>
|
||||
<path d="M19.8 6.267a1 1 0 01-1.414 0l-1.411-1.414a1 1 0 010-1.415l.186-.186a1 1 0 01.391-.242l4.536-1.51a.927.927 0 01.949 1.535z" />
|
||||
<path d="M16.975 4.853L9.55 12.277l1.414 1.414 7.425-7.424" />
|
||||
<path d="M11.187 10.64a2.881 2.881 0 01-.8-2.538 6.278 6.278 0 01.738-1.99A1.15 1.15 0 009.3 4.749a6.56 6.56 0 00-1.91 3.406c-.22 1.038-1 2.463-2.1 2.485a4.638 4.638 0 00-4.6 4.746 5.927 5.927 0 001.812 4.249l1.1 1.1a5.93 5.93 0 004.249 1.812 4.639 4.639 0 004.746-4.6c0-1.1 1.235-1.789 2.286-1.755a4.13 4.13 0 003.324-1.269 1.1 1.1 0 00-.719-1.846c-3.306-.254-4-.141-4.891-1.029M7.782 13.338l2.122 2.121" />
|
||||
<path d="M4.954 14.753l3.535 3.535-1.768 1.768-3.535-3.535z" />
|
||||
</g>
|
||||
),
|
||||
};
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// @flow
|
||||
import * as REACTION_TYPES from 'constants/reactions';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import Button from 'component/button';
|
||||
import { formatNumberWithCommas } from 'util/number';
|
||||
import NudgeFloating from 'component/nudgeFloating';
|
||||
import { SIMPLE_SITE } from 'config';
|
||||
|
||||
type Props = {
|
||||
claim: StreamClaim,
|
||||
|
@ -15,18 +17,51 @@ type Props = {
|
|||
likeCount: number,
|
||||
dislikeCount: number,
|
||||
myReaction: ?string,
|
||||
livestream?: boolean,
|
||||
};
|
||||
|
||||
function FileReactions(props: Props) {
|
||||
const { claim, uri, doFetchReactions, doReactionLike, doReactionDislike, likeCount, dislikeCount } = props;
|
||||
const {
|
||||
claim,
|
||||
uri,
|
||||
doFetchReactions,
|
||||
doReactionLike,
|
||||
doReactionDislike,
|
||||
myReaction,
|
||||
likeCount,
|
||||
dislikeCount,
|
||||
livestream,
|
||||
} = props;
|
||||
|
||||
const claimId = claim && claim.claim_id;
|
||||
const channel = claim && claim.signing_channel && claim.signing_channel.name;
|
||||
const isCollection = claim && claim.value_type === 'collection'; // hack because nudge gets cut off by card on cols.
|
||||
const likeIcon = SIMPLE_SITE ? (myReaction === REACTION_TYPES.LIKE ? ICONS.FIRE_ACTIVE : ICONS.FIRE) : ICONS.UPVOTE;
|
||||
const dislikeIcon = SIMPLE_SITE
|
||||
? myReaction === REACTION_TYPES.DISLIKE
|
||||
? ICONS.SLIME_ACTIVE
|
||||
: ICONS.SLIME
|
||||
: ICONS.DOWNVOTE;
|
||||
React.useEffect(() => {
|
||||
if (claimId) {
|
||||
function fetchReactions() {
|
||||
doFetchReactions(claimId);
|
||||
}
|
||||
}, [claimId, doFetchReactions]);
|
||||
|
||||
let fetchInterval;
|
||||
if (claimId) {
|
||||
fetchReactions();
|
||||
|
||||
if (livestream) {
|
||||
fetchInterval = setInterval(fetchReactions, 45000);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (fetchInterval) {
|
||||
clearInterval(fetchInterval);
|
||||
}
|
||||
};
|
||||
}, [claimId, doFetchReactions, livestream]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -41,20 +76,46 @@ function FileReactions(props: Props) {
|
|||
title={__('I like this')}
|
||||
requiresAuth={IS_WEB}
|
||||
authSrc="filereaction_like"
|
||||
className={classnames('button--file-action')}
|
||||
label={formatNumberWithCommas(likeCount, 0)}
|
||||
className={classnames('button--file-action', { 'button--fire': myReaction === REACTION_TYPES.LIKE })}
|
||||
label={
|
||||
<>
|
||||
{myReaction === REACTION_TYPES.LIKE && SIMPLE_SITE && (
|
||||
<>
|
||||
<div className="button__fire-glow" />
|
||||
<div className="button__fire-particle1" />
|
||||
<div className="button__fire-particle2" />
|
||||
<div className="button__fire-particle3" />
|
||||
<div className="button__fire-particle4" />
|
||||
<div className="button__fire-particle5" />
|
||||
<div className="button__fire-particle6" />
|
||||
</>
|
||||
)}
|
||||
{formatNumberWithCommas(likeCount, 0)}
|
||||
</>
|
||||
}
|
||||
iconSize={18}
|
||||
icon={ICONS.UPVOTE}
|
||||
icon={likeIcon}
|
||||
onClick={() => doReactionLike(uri)}
|
||||
/>
|
||||
<Button
|
||||
requiresAuth={IS_WEB}
|
||||
authSrc={'filereaction_dislike'}
|
||||
title={__('I dislike this')}
|
||||
className={classnames('button--file-action')}
|
||||
label={formatNumberWithCommas(dislikeCount, 0)}
|
||||
className={classnames('button--file-action', { 'button--slime': myReaction === REACTION_TYPES.DISLIKE })}
|
||||
label={
|
||||
<>
|
||||
{myReaction === REACTION_TYPES.DISLIKE && SIMPLE_SITE && (
|
||||
<>
|
||||
<div className="button__slime-stain" />
|
||||
<div className="button__slime-drop1" />
|
||||
<div className="button__slime-drop2" />
|
||||
</>
|
||||
)}
|
||||
{formatNumberWithCommas(dislikeCount, 0)}
|
||||
</>
|
||||
}
|
||||
iconSize={18}
|
||||
icon={ICONS.DOWNVOTE}
|
||||
icon={dislikeIcon}
|
||||
onClick={() => doReactionDislike(uri)}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -22,7 +22,7 @@ function FileSubtitle(props: Props) {
|
|||
<FileViewCount uri={uri} livestream={livestream} activeViewers={activeViewers} isLive={isLive} />
|
||||
</div>
|
||||
|
||||
<FileActions uri={uri} />
|
||||
<FileActions uri={uri} hideRepost={livestream} livestream={livestream} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import FilePrice from 'component/filePrice';
|
|||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { withRouter } from 'react-router';
|
||||
import { URL } from 'config';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import Logo from 'component/logo';
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
|
@ -36,7 +36,9 @@ function FileViewerEmbeddedTitle(props: Props) {
|
|||
{...contentLinkProps}
|
||||
/>
|
||||
<div className="file-viewer__embedded-info">
|
||||
<Button className="file-viewer__overlay-logo" icon={ICONS.LBRY} aria-label={__('Home')} {...lbryLinkProps} />
|
||||
<Button className="file-viewer__overlay-logo" aria-label={__('Home')} {...lbryLinkProps}>
|
||||
<Logo type={'embed'} />
|
||||
</Button>
|
||||
{isInApp && <FilePrice uri={uri} />}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import { LOGO_TITLE, ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM, ENABLE_UI_NOTIFICATIONS } from 'config';
|
||||
import { ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM, ENABLE_UI_NOTIFICATIONS } from 'config';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import { SETTINGS } from 'lbry-redux';
|
||||
import * as PAGES from 'constants/pages';
|
||||
|
@ -16,6 +16,7 @@ import NotificationBubble from 'component/notificationBubble';
|
|||
import NotificationHeaderButton from 'component/notificationHeaderButton';
|
||||
import ChannelThumbnail from 'component/channelThumbnail';
|
||||
import SkipNavigationButton from 'component/skipNavigationButton';
|
||||
import Logo from 'component/logo';
|
||||
// @if TARGET='app'
|
||||
import { remote } from 'electron';
|
||||
import { IS_MAC } from 'component/app/view';
|
||||
|
@ -254,15 +255,8 @@ const Header = (props: Props) => {
|
|||
</span>
|
||||
)}
|
||||
<Button
|
||||
className="header__navigation-item header__navigation-item--lbry"
|
||||
// @if TARGET='app'
|
||||
aria-label={__('Home')}
|
||||
label={'LBRY'}
|
||||
// @endif
|
||||
// @if TARGET='web'
|
||||
label={LOGO_TITLE} // eslint-disable-line
|
||||
// @endif
|
||||
icon={ICONS.LBRY}
|
||||
className="header__navigation-item header__navigation-item--lbry"
|
||||
onClick={() => {
|
||||
if (history.location.pathname === '/') window.location.reload();
|
||||
}}
|
||||
|
@ -272,8 +266,9 @@ const Header = (props: Props) => {
|
|||
}}
|
||||
// @endif
|
||||
{...homeButtonNavigationProps}
|
||||
/>
|
||||
|
||||
>
|
||||
<Logo />
|
||||
</Button>
|
||||
{!authHeader && (
|
||||
<div className="header__center">
|
||||
{/* @if TARGET='app' */}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import { BITWAVE_EMBED_URL } from 'constants/livestream';
|
||||
import { LIVESTREAM_EMBED_URL } from 'constants/livestream';
|
||||
import React from 'react';
|
||||
import FileTitleSection from 'component/fileTitleSection';
|
||||
import LivestreamComments from 'component/livestreamComments';
|
||||
|
@ -29,7 +29,7 @@ export default function LivestreamLayout(props: Props) {
|
|||
<div className="file-render file-render--video livestream">
|
||||
<div className="file-viewer">
|
||||
<iframe
|
||||
src={`${BITWAVE_EMBED_URL}/${channelClaimId}?skin=odysee&autoplay=1`}
|
||||
src={`${LIVESTREAM_EMBED_URL}/${channelClaimId}?skin=odysee&autoplay=1`}
|
||||
scrolling="no"
|
||||
allowFullScreen
|
||||
/>
|
||||
|
@ -38,17 +38,19 @@ export default function LivestreamLayout(props: Props) {
|
|||
|
||||
{Boolean(chatDisabled) && (
|
||||
<div className="help--notice">
|
||||
{__('%channel% has disabled chat for this stream. Enjoy the stream!', {
|
||||
channel: channelName || __('This channel'),
|
||||
})}
|
||||
{channelName
|
||||
? __('%channelName% has disabled chat for this stream. Enjoy the stream!', { channelName })
|
||||
: __('This channel has disabled chat for this stream. Enjoy the stream!')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLive && (
|
||||
<div className="help--notice">
|
||||
{__("%channel% isn't live right now, but the chat is! Check back later to watch the stream.", {
|
||||
channel: channelName || __('This channel'),
|
||||
})}
|
||||
{channelName
|
||||
? __("%channelName% isn't live right now, but the chat is! Check back later to watch the stream.", {
|
||||
channelName,
|
||||
})
|
||||
: __("This channel isn't live right now, but the chat is! Check back later to watch the stream.")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import { BITWAVE_LIVE_API } from 'constants/livestream';
|
||||
import { LIVESTREAM_LIVE_API } from 'constants/livestream';
|
||||
import * as CS from 'constants/claim_search';
|
||||
import React from 'react';
|
||||
import Card from 'component/common/card';
|
||||
|
@ -39,8 +39,8 @@ export default function LivestreamLink(props: Props) {
|
|||
|
||||
React.useEffect(() => {
|
||||
function fetchIsStreaming() {
|
||||
// $FlowFixMe Bitwave's API can handle garbage
|
||||
fetch(`${BITWAVE_LIVE_API}/${livestreamChannelId}`)
|
||||
// $FlowFixMe livestream API can handle garbage
|
||||
fetch(`${LIVESTREAM_LIVE_API}/${livestreamChannelId}`)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (res && res.success && res.data && res.data.live) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import * as ICONS from 'constants/icons';
|
||||
import { BITWAVE_LIVE_API } from 'constants/livestream';
|
||||
import { LIVESTREAM_LIVE_API } from 'constants/livestream';
|
||||
import React from 'react';
|
||||
import Icon from 'component/common/icon';
|
||||
import Spinner from 'component/spinner';
|
||||
|
@ -14,7 +14,7 @@ export default function LivestreamList() {
|
|||
|
||||
React.useEffect(() => {
|
||||
function checkCurrentLivestreams() {
|
||||
fetch(BITWAVE_LIVE_API)
|
||||
fetch(LIVESTREAM_LIVE_API)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
setLoading(false);
|
||||
|
|
10
ui/component/logo/index.js
Normal file
10
ui/component/logo/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { connect } from 'react-redux';
|
||||
import Logo from './view';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { SETTINGS } from 'lbry-redux';
|
||||
|
||||
const select = (state, props) => ({
|
||||
currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state),
|
||||
});
|
||||
|
||||
export default connect(select)(Logo);
|
51
ui/component/logo/view.jsx
Normal file
51
ui/component/logo/view.jsx
Normal file
|
@ -0,0 +1,51 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import { LOGO_TITLE, LOGO, LOGO_TEXT_LIGHT, LOGO_TEXT_DARK } from 'config';
|
||||
import Icon from 'component/common/icon';
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
|
||||
type Props = {
|
||||
type: string,
|
||||
currentTheme: string,
|
||||
};
|
||||
|
||||
export default function Logo(props: Props) {
|
||||
const { type, currentTheme } = props;
|
||||
const isMobile = useIsMobile();
|
||||
const defaultWithLabel = (
|
||||
<>
|
||||
<Icon icon={ICONS.LBRY} />
|
||||
{/* @if TARGET='app' */}
|
||||
<div className={'button__label'}>{'LBRY'}</div>
|
||||
{/* @endif */}
|
||||
{/* @if TARGET='web' */}
|
||||
<div className={'button__label'}>{LOGO_TITLE}</div>
|
||||
{/* @endif */}
|
||||
</>
|
||||
);
|
||||
|
||||
if (type === 'small' || (isMobile && type !== 'embed')) {
|
||||
return LOGO ? <img src={LOGO} /> : <Icon icon={ICONS.LBRY} />;
|
||||
} else if (type === 'embed') {
|
||||
if (LOGO_TEXT_LIGHT) {
|
||||
return (
|
||||
<>
|
||||
<img src={LOGO_TEXT_LIGHT} />
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return defaultWithLabel;
|
||||
}
|
||||
} else {
|
||||
if (LOGO_TEXT_LIGHT && LOGO_TEXT_DARK) {
|
||||
return (
|
||||
<>
|
||||
<img src={currentTheme === 'light' ? LOGO_TEXT_DARK : LOGO_TEXT_LIGHT} />
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return defaultWithLabel;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,8 +104,10 @@ function PostViewer(props: Props) {
|
|||
|
||||
<ClaimAuthor uri={uri} />
|
||||
|
||||
<div className="file-render--post-container">
|
||||
<FileRenderInitiator uri={uri} />
|
||||
<FileRenderInline uri={uri} />
|
||||
</div>
|
||||
<FileActions uri={uri} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -28,7 +28,7 @@ import * as PUBLISH_MODES from 'constants/publish_types';
|
|||
import { useHistory } from 'react-router';
|
||||
import Spinner from 'component/spinner';
|
||||
import { toHex } from 'util/hex';
|
||||
import { BITWAVE_REPLAY_API } from 'constants/livestream';
|
||||
import { LIVESTREAM_REPLAY_API } from 'constants/livestream';
|
||||
|
||||
// @if TARGET='app'
|
||||
import fs from 'fs';
|
||||
|
@ -182,9 +182,9 @@ function PublishForm(props: Props) {
|
|||
}
|
||||
} else {
|
||||
if (editingURI) {
|
||||
customSubtitle = __('Update your video');
|
||||
customSubtitle = __('Update your content');
|
||||
} else {
|
||||
customSubtitle = __('Upload that unlabeled video you found behind the TV in 1991');
|
||||
customSubtitle = __('Upload that unlabeled video or cassette you found behind the TV in 1991');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,7 +274,7 @@ function PublishForm(props: Props) {
|
|||
// move this to lbryinc OR to a file under ui, and/or provide a standardized livestreaming config.
|
||||
function fetchLivestreams(channelId, signature, timestamp) {
|
||||
setCheckingLivestreams(true);
|
||||
fetch(`${BITWAVE_REPLAY_API}/${channelId}?signature=${signature || ''}&signing_ts=${timestamp || ''}`) // claimChannelId
|
||||
fetch(`${LIVESTREAM_REPLAY_API}/${channelId}?signature=${signature || ''}&signing_ts=${timestamp || ''}`) // claimChannelId
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (!res || !res.data) {
|
||||
|
@ -566,6 +566,7 @@ function PublishForm(props: Props) {
|
|||
<Button
|
||||
key={String(modeName)}
|
||||
icon={modeName}
|
||||
iconSize={18}
|
||||
label={__(MODE_TO_I18N_STR[String(modeName)] || '---')}
|
||||
button="alt"
|
||||
onClick={() => {
|
||||
|
|
|
@ -6,12 +6,11 @@ import Nag from 'component/common/nag';
|
|||
import { parseURI } from 'lbry-redux';
|
||||
import Button from 'component/button';
|
||||
import Card from 'component/common/card';
|
||||
import { AUTO_FOLLOW_CHANNELS, CUSTOM_HOMEPAGE } from 'config';
|
||||
import { AUTO_FOLLOW_CHANNELS, CUSTOM_HOMEPAGE, SIMPLE_SITE, SITE_NAME } from 'config';
|
||||
|
||||
type Props = {
|
||||
subscribedChannels: Array<Subscription>,
|
||||
onContinue: () => void,
|
||||
onBack: () => void,
|
||||
channelSubscribe: (sub: Subscription) => void,
|
||||
homepageData: any,
|
||||
prefsReady: boolean,
|
||||
|
@ -22,7 +21,7 @@ const channelsToSubscribe = AUTO_FOLLOW_CHANNELS.trim()
|
|||
.filter((x) => x !== '');
|
||||
|
||||
function UserChannelFollowIntro(props: Props) {
|
||||
const { subscribedChannels, channelSubscribe, onContinue, onBack, homepageData, prefsReady } = props;
|
||||
const { subscribedChannels, channelSubscribe, onContinue, homepageData, prefsReady } = props;
|
||||
const { PRIMARY_CONTENT } = homepageData;
|
||||
let channelIds;
|
||||
if (PRIMARY_CONTENT && CUSTOM_HOMEPAGE) {
|
||||
|
@ -49,25 +48,27 @@ function UserChannelFollowIntro(props: Props) {
|
|||
<Card
|
||||
title={__('Find channels to follow')}
|
||||
subtitle={__(
|
||||
'LBRY works better if you find and follow a couple creators you like. You can also block channels you never want to see.'
|
||||
'%SITE_NAME% works better if you find and follow a couple creators you like. You can also block channels you never want to see.',
|
||||
{ SITE_NAME }
|
||||
)}
|
||||
actions={
|
||||
<React.Fragment>
|
||||
<div className="section__actions--between">
|
||||
<Button button="secondary" onClick={onBack} label={__('Back')} />
|
||||
<div className="section__body">
|
||||
<ClaimListDiscover
|
||||
hideFilters={SIMPLE_SITE}
|
||||
meta={
|
||||
<Button
|
||||
button={subscribedChannels.length < 1 ? 'alt' : 'primary'}
|
||||
onClick={onContinue}
|
||||
label={subscribedChannels.length < 1 ? __('Skip') : __('Continue')}
|
||||
/>
|
||||
</div>
|
||||
<div className="section__body">
|
||||
<ClaimListDiscover
|
||||
defaultOrderBy={CS.ORDER_BY_TOP}
|
||||
}
|
||||
defaultOrderBy={SIMPLE_SITE ? CS.ORDER_BY_TOP : CS.ORDER_BY_TRENDING}
|
||||
defaultFreshness={CS.FRESH_ALL}
|
||||
claimType="channel"
|
||||
claimIds={CUSTOM_HOMEPAGE && channelIds ? channelIds : undefined}
|
||||
defaultTags={followingCount > 3 ? CS.TAGS_FOLLOWED : undefined}
|
||||
maxPages={SIMPLE_SITE ? 3 : undefined}
|
||||
/>
|
||||
{followingCount > 0 && (
|
||||
<Nag
|
||||
|
|
|
@ -86,13 +86,12 @@ class DocumentViewer extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { error, loading, content } = this.state;
|
||||
const { error, content } = this.state;
|
||||
const isReady = content && !error;
|
||||
const errorMessage = __("Sorry, looks like we can't load the document.");
|
||||
|
||||
return (
|
||||
<div className="file-viewer file-viewer--document">
|
||||
{loading && !error && <div className="placeholder--text-document" />}
|
||||
{error && <LoadingScreen status={errorMessage} spinner={!error} />}
|
||||
{isReady && this.renderDocument()}
|
||||
</div>
|
||||
|
|
|
@ -497,7 +497,6 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
}
|
||||
|
||||
function detectFileType() {
|
||||
console.log(`Detecting file type via pre-fetch...`);
|
||||
return new Promise(async (res, rej) => {
|
||||
try {
|
||||
const response = await fetch(source, { method: 'HEAD', cache: 'no-store' });
|
||||
|
@ -514,8 +513,6 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
finalSource = response.url;
|
||||
}
|
||||
|
||||
console.log(`File type is: ${finalType}`);
|
||||
|
||||
// Modify video source in options
|
||||
videoJsOptions.sources = [
|
||||
{
|
||||
|
@ -526,7 +523,6 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
|
||||
return res(videoJsOptions);
|
||||
} catch (error) {
|
||||
console.error(`Failed to pre-fetch video!`);
|
||||
return rej(error);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -25,6 +25,8 @@ if (STRIPE_PUBLIC_KEY.indexOf('pk_live') > -1) {
|
|||
}
|
||||
|
||||
const DEFAULT_TIP_AMOUNTS = [1, 5, 25, 100];
|
||||
const MINIMUM_FIAT_TIP = 1;
|
||||
const MAXIMUM_FIAT_TIP = 1000;
|
||||
|
||||
const TAB_BOOST = 'TabBoost';
|
||||
const TAB_FIAT = 'TabFiat';
|
||||
|
@ -186,8 +188,7 @@ function WalletSendTip(props: Props) {
|
|||
|
||||
React.useEffect(() => {
|
||||
// Regex for number up to 8 decimal places
|
||||
const regexp = RegExp(/^(\d*([.]\d{0,8})?)$/);
|
||||
const validTipInput = regexp.test(String(tipAmount));
|
||||
let regexp;
|
||||
let tipError;
|
||||
|
||||
if (tipAmount === 0) {
|
||||
|
@ -198,8 +199,13 @@ function WalletSendTip(props: Props) {
|
|||
|
||||
// if it's not fiat, aka it's boost or lbc tip
|
||||
else if (activeTab !== TAB_FIAT) {
|
||||
regexp = RegExp(/^(\d*([.]\d{0,8})?)$/);
|
||||
const validTipInput = regexp.test(String(tipAmount));
|
||||
|
||||
if (!validTipInput) {
|
||||
tipError = __('Amount must have no more than 8 decimal places');
|
||||
} else if (!validTipInput) {
|
||||
tipError = __('Amount must have no more than 8 decimal places');
|
||||
} else if (tipAmount === balance) {
|
||||
tipError = __('Please decrease the amount to account for transaction fees');
|
||||
} else if (tipAmount > balance) {
|
||||
|
@ -209,9 +215,14 @@ function WalletSendTip(props: Props) {
|
|||
}
|
||||
// if tip fiat tab
|
||||
} else {
|
||||
if (tipAmount < 1) {
|
||||
regexp = RegExp(/^(\d*([.]\d{0,2})?)$/);
|
||||
const validTipInput = regexp.test(String(tipAmount));
|
||||
|
||||
if (!validTipInput) {
|
||||
tipError = __('Amount must have no more than 2 decimal places');
|
||||
} else if (tipAmount < MINIMUM_FIAT_TIP) {
|
||||
tipError = __('Amount must be at least one dollar');
|
||||
} else if (tipAmount > 1000) {
|
||||
} else if (tipAmount > MAXIMUM_FIAT_TIP) {
|
||||
tipError = __('Amount cannot be over 1000 dollars');
|
||||
}
|
||||
}
|
||||
|
@ -544,7 +555,7 @@ function WalletSendTip(props: Props) {
|
|||
</React.Fragment>
|
||||
}
|
||||
className="form-field--price-amount"
|
||||
error={tipError && activeTab !== TAB_FIAT}
|
||||
error={tipError}
|
||||
min="0"
|
||||
step="any"
|
||||
type="number"
|
||||
|
@ -565,7 +576,7 @@ function WalletSendTip(props: Props) {
|
|||
disabled={
|
||||
fetchingChannels ||
|
||||
isPending ||
|
||||
(tipError && activeTab !== TAB_FIAT) ||
|
||||
tipError ||
|
||||
!tipAmount ||
|
||||
(activeTab === TAB_FIAT && (!hasCardSaved || !canReceiveFiatTip))
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ type Props = {
|
|||
uri: string,
|
||||
onTipErrorChange: (string) => void,
|
||||
activeTab: string,
|
||||
shouldDisableReviewButton: (boolean) => void
|
||||
shouldDisableReviewButton: (boolean) => void,
|
||||
};
|
||||
|
||||
function WalletTipAmountSelector(props: Props) {
|
||||
|
@ -46,7 +46,7 @@ function WalletTipAmountSelector(props: Props) {
|
|||
const [hasCardSaved, setHasSavedCard] = usePersistedState('comment-support:hasCardSaved', false);
|
||||
|
||||
// if it's fiat but there's no card saved OR the creator can't receive fiat tips
|
||||
const shouldDisableFiatSelectors = (activeTab === TAB_FIAT && (!hasCardSaved || !canReceiveFiatTip));
|
||||
const shouldDisableFiatSelectors = activeTab === TAB_FIAT && (!hasCardSaved || !canReceiveFiatTip);
|
||||
|
||||
/**
|
||||
* whether tip amount selection/review functionality should be disabled
|
||||
|
@ -120,9 +120,8 @@ function WalletTipAmountSelector(props: Props) {
|
|||
// setHasSavedCard(false);
|
||||
// setCanReceiveFiatTip(true);
|
||||
|
||||
const regexp = RegExp(/^(\d*([.]\d{0,8})?)$/);
|
||||
const validTipInput = regexp.test(String(amount));
|
||||
let tipError = '';
|
||||
let regexp,
|
||||
tipError = '';
|
||||
|
||||
if (amount === 0) {
|
||||
tipError = __('Amount must be a positive number');
|
||||
|
@ -132,6 +131,9 @@ function WalletTipAmountSelector(props: Props) {
|
|||
|
||||
// if it's not fiat, aka it's boost or lbc tip
|
||||
else if (activeTab !== TAB_FIAT) {
|
||||
regexp = RegExp(/^(\d*([.]\d{0,8})?)$/);
|
||||
const validTipInput = regexp.test(String(amount));
|
||||
|
||||
if (!validTipInput) {
|
||||
tipError = __('Amount must have no more than 8 decimal places');
|
||||
} else if (amount === balance) {
|
||||
|
@ -143,7 +145,12 @@ function WalletTipAmountSelector(props: Props) {
|
|||
}
|
||||
// if tip fiat tab
|
||||
} else {
|
||||
if (amount < 1) {
|
||||
regexp = RegExp(/^(\d*([.]\d{0,2})?)$/);
|
||||
const validTipInput = regexp.test(String(amount));
|
||||
|
||||
if (!validTipInput) {
|
||||
tipError = __('Amount must have no more than 2 decimal places');
|
||||
} else if (amount < 1) {
|
||||
tipError = __('Amount must be at least one dollar');
|
||||
} else if (amount > 1000) {
|
||||
tipError = __('Amount cannot be over 1000 dollars');
|
||||
|
@ -154,8 +161,10 @@ function WalletTipAmountSelector(props: Props) {
|
|||
onTipErrorChange(tipError);
|
||||
}, [amount, balance, setTipError, activeTab]);
|
||||
|
||||
// parse number as float and sets it in the parent component
|
||||
function handleCustomPriceChange(amount: number) {
|
||||
const tipAmount = parseFloat(amount);
|
||||
|
||||
onChange(tipAmount);
|
||||
}
|
||||
|
||||
|
@ -229,6 +238,7 @@ function WalletTipAmountSelector(props: Props) {
|
|||
</>
|
||||
)}
|
||||
|
||||
{/* custom number input form */}
|
||||
{useCustomTip && (
|
||||
<div className="comment__tip-input">
|
||||
<FormField
|
||||
|
|
|
@ -164,3 +164,4 @@ export const TIME = 'time';
|
|||
export const GLOBE = 'globe';
|
||||
export const RSS = 'rss';
|
||||
export const STAR = 'star';
|
||||
export const MUSIC = 'MusicCategory';
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export const BITWAVE_EMBED_URL = 'https://bitwave.tv/odysee';
|
||||
export const BITWAVE_LIVE_API = 'https://api.bitwave.tv/v1/odysee/live';
|
||||
export const BITWAVE_REPLAY_API = 'https://api.bitwave.tv/v1/replays/odysee';
|
||||
export const LIVESTREAM_EMBED_URL = 'https://player.live.odysee.com/odysee';
|
||||
export const LIVESTREAM_LIVE_API = 'https://api.live.odysee.com/v1/odysee/live';
|
||||
export const LIVESTREAM_REPLAY_API = 'https://api.live.odysee.com/v1/replays/odysee';
|
||||
export const LIVESTREAM_RTMP_URL = 'rtmp://stream.odysee.com/live';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { BITWAVE_LIVE_API } from 'constants/livestream';
|
||||
import { LIVESTREAM_LIVE_API } from 'constants/livestream';
|
||||
|
||||
/**
|
||||
* Gets latest livestream info list. Returns null (instead of a blank object)
|
||||
|
@ -16,7 +16,7 @@ export default function useGetLivestreams(minViewers: number = 0, refreshMs: num
|
|||
|
||||
React.useEffect(() => {
|
||||
function checkCurrentLivestreams() {
|
||||
fetch(BITWAVE_LIVE_API)
|
||||
fetch(LIVESTREAM_LIVE_API)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
setLoading(false);
|
||||
|
|
|
@ -29,11 +29,15 @@ import TruncatedText from 'component/common/truncated-text';
|
|||
import PlaceholderTx from 'static/img/placeholderTx.gif';
|
||||
|
||||
export const PAGE_VIEW_QUERY = `view`;
|
||||
const CONTENT_PAGE = 'content';
|
||||
const LISTS_PAGE = 'lists';
|
||||
const ABOUT_PAGE = `about`;
|
||||
export const DISCUSSION_PAGE = `discussion`;
|
||||
const EDIT_PAGE = 'edit';
|
||||
|
||||
const PAGE = {
|
||||
CONTENT: 'content',
|
||||
LISTS: 'lists',
|
||||
ABOUT: 'about',
|
||||
DISCUSSION: DISCUSSION_PAGE,
|
||||
EDIT: 'edit',
|
||||
};
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
|
@ -84,9 +88,9 @@ function ChannelPage(props: Props) {
|
|||
} = useHistory();
|
||||
const [viewBlockedChannel, setViewBlockedChannel] = React.useState(false);
|
||||
const urlParams = new URLSearchParams(search);
|
||||
const currentView = urlParams.get(PAGE_VIEW_QUERY) || undefined;
|
||||
const currentView = urlParams.get(PAGE_VIEW_QUERY) || PAGE.CONTENT;
|
||||
const [discussionWasMounted, setDiscussionWasMounted] = React.useState(false);
|
||||
const editing = urlParams.get(PAGE_VIEW_QUERY) === EDIT_PAGE;
|
||||
const editing = urlParams.get(PAGE_VIEW_QUERY) === PAGE.EDIT;
|
||||
const { channelName } = parseURI(uri);
|
||||
const { permanent_url: permanentUrl } = claim;
|
||||
const claimId = claim.claim_id;
|
||||
|
@ -144,16 +148,16 @@ function ChannelPage(props: Props) {
|
|||
// would alter the Tab label's role attribute, which should stay role="tab" to work with keyboards/screen readers.
|
||||
let tabIndex;
|
||||
switch (currentView) {
|
||||
case CONTENT_PAGE:
|
||||
case PAGE.CONTENT:
|
||||
tabIndex = 0;
|
||||
break;
|
||||
case LISTS_PAGE:
|
||||
case PAGE.LISTS:
|
||||
tabIndex = 1;
|
||||
break;
|
||||
case ABOUT_PAGE:
|
||||
case PAGE.ABOUT:
|
||||
tabIndex = 2;
|
||||
break;
|
||||
case DISCUSSION_PAGE:
|
||||
case PAGE.DISCUSSION:
|
||||
tabIndex = 3;
|
||||
break;
|
||||
default:
|
||||
|
@ -166,20 +170,20 @@ function ChannelPage(props: Props) {
|
|||
let search = '?';
|
||||
|
||||
if (newTabIndex === 0) {
|
||||
search += `${PAGE_VIEW_QUERY}=${CONTENT_PAGE}`;
|
||||
search += `${PAGE_VIEW_QUERY}=${PAGE.CONTENT}`;
|
||||
} else if (newTabIndex === 1) {
|
||||
search += `${PAGE_VIEW_QUERY}=${LISTS_PAGE}`;
|
||||
search += `${PAGE_VIEW_QUERY}=${PAGE.LISTS}`;
|
||||
} else if (newTabIndex === 2) {
|
||||
search += `${PAGE_VIEW_QUERY}=${ABOUT_PAGE}`;
|
||||
search += `${PAGE_VIEW_QUERY}=${PAGE.ABOUT}`;
|
||||
} else {
|
||||
search += `${PAGE_VIEW_QUERY}=${DISCUSSION_PAGE}`;
|
||||
search += `${PAGE_VIEW_QUERY}=${PAGE.DISCUSSION}`;
|
||||
}
|
||||
|
||||
push(`${url}${search}`);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (currentView === DISCUSSION_PAGE) {
|
||||
if (currentView === PAGE.DISCUSSION) {
|
||||
setDiscussionWasMounted(true);
|
||||
}
|
||||
}, [currentView]);
|
||||
|
@ -244,7 +248,7 @@ function ChannelPage(props: Props) {
|
|||
<Button
|
||||
button="alt"
|
||||
title={__('Edit')}
|
||||
onClick={() => push(`?${PAGE_VIEW_QUERY}=${EDIT_PAGE}`)}
|
||||
onClick={() => push(`?${PAGE_VIEW_QUERY}=${PAGE.EDIT}`)}
|
||||
icon={ICONS.EDIT}
|
||||
iconSize={18}
|
||||
disabled={pending}
|
||||
|
@ -287,14 +291,17 @@ function ChannelPage(props: Props) {
|
|||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
{currentView === PAGE.CONTENT && (
|
||||
<ChannelContent
|
||||
uri={uri}
|
||||
channelIsBlackListed={channelIsBlackListed}
|
||||
viewHiddenChannels
|
||||
empty={<section className="main--empty">{__('No Content Found')}</section>}
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
{currentView === PAGE.LISTS && (
|
||||
<ChannelContent
|
||||
claimType={'collection'}
|
||||
uri={uri}
|
||||
|
@ -302,12 +309,13 @@ function ChannelPage(props: Props) {
|
|||
viewHiddenChannels
|
||||
empty={collectionEmpty}
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<ChannelAbout uri={uri} />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
{(discussionWasMounted || currentView === DISCUSSION_PAGE) && <ChannelDiscussion uri={uri} />}
|
||||
{(discussionWasMounted || currentView === PAGE.DISCUSSION) && <ChannelDiscussion uri={uri} />}
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
|
|
|
@ -127,6 +127,9 @@ function ChannelsFollowingDiscover(props: Props) {
|
|||
claimType={CS.CLAIM_CHANNEL}
|
||||
claimIds={CUSTOM_HOMEPAGE && channelIds ? channelIds : undefined}
|
||||
scrollAnchor={MORE_CHANNELS_ANCHOR}
|
||||
maxPages={SIMPLE_SITE ? 3 : undefined}
|
||||
hideFilters={SIMPLE_SITE}
|
||||
header={SIMPLE_SITE ? <h1 className="section__title">{__('Moon cheese is an acquired taste')}</h1> : undefined}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { SHOW_ADS, DOMAIN, SIMPLE_SITE, ENABLE_NO_SOURCE_CLAIMS } from 'config';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import * as PAGES from 'constants/pages';
|
||||
import * as CS from 'constants/claim_search';
|
||||
import React, { useRef } from 'react';
|
||||
import Page from 'component/page';
|
||||
import ClaimListDiscover from 'component/claimListDiscover';
|
||||
|
@ -11,11 +12,11 @@ import { useIsMobile } from 'effects/use-screensize';
|
|||
import analytics from 'analytics';
|
||||
import HiddenNsfw from 'component/common/hidden-nsfw';
|
||||
import Icon from 'component/common/icon';
|
||||
import * as CS from 'constants/claim_search';
|
||||
import Ads from 'web/component/ads';
|
||||
import LbcSymbol from 'component/common/lbc-symbol';
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
import useGetLivestreams from 'effects/use-get-livestreams';
|
||||
import moment from 'moment';
|
||||
|
||||
type Props = {
|
||||
location: { search: string },
|
||||
|
@ -52,6 +53,8 @@ function DiscoverPage(props: Props) {
|
|||
const tags = tagsQuery ? tagsQuery.split(',') : null;
|
||||
const repostedClaimIsResolved = repostedUri && repostedClaim;
|
||||
|
||||
const discoverIcon = SIMPLE_SITE ? ICONS.WILD_WEST : ICONS.DISCOVER;
|
||||
const discoverLabel = SIMPLE_SITE ? __('Wild West') : __('All Content');
|
||||
// Eventually allow more than one tag on this page
|
||||
// Restricting to one to make follow/unfollow simpler
|
||||
const tag = (tags && tags[0]) || null;
|
||||
|
@ -97,8 +100,8 @@ function DiscoverPage(props: Props) {
|
|||
} else {
|
||||
headerLabel = (
|
||||
<span>
|
||||
<Icon icon={(dynamicRouteProps && dynamicRouteProps.icon) || ICONS.DISCOVER} size={10} />
|
||||
{(dynamicRouteProps && dynamicRouteProps.title) || __('All Content')}
|
||||
<Icon icon={(dynamicRouteProps && dynamicRouteProps.icon) || discoverIcon} size={10} />
|
||||
{(dynamicRouteProps && dynamicRouteProps.title) || discoverLabel}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -106,9 +109,11 @@ function DiscoverPage(props: Props) {
|
|||
return (
|
||||
<Page noFooter fullWidthPage={tileLayout}>
|
||||
<ClaimListDiscover
|
||||
limitClaimsPerChannel={3}
|
||||
hideAdvancedFilter={SIMPLE_SITE}
|
||||
hideFilters={SIMPLE_SITE ? !dynamicRouteProps : undefined}
|
||||
header={repostedUri ? <span /> : undefined}
|
||||
tileLayout={repostedUri ? false : tileLayout}
|
||||
defaultOrderBy={SIMPLE_SITE ? (dynamicRouteProps ? undefined : CS.ORDER_BY_TRENDING) : undefined}
|
||||
claimType={claimType ? [claimType] : undefined}
|
||||
headerLabel={headerLabel}
|
||||
tags={tags}
|
||||
|
@ -117,9 +122,24 @@ function DiscoverPage(props: Props) {
|
|||
injectedItem={
|
||||
SHOW_ADS && IS_WEB ? (SIMPLE_SITE ? false : !isAuthenticated && <Ads small type={'video'} />) : false
|
||||
}
|
||||
// Assume wild west page if no dynamicRouteProps
|
||||
// Not a very good solution, but just doing it for now
|
||||
// until we are sure this page will stay around
|
||||
releaseTime={
|
||||
SIMPLE_SITE
|
||||
? !dynamicRouteProps && `>${Math.floor(moment().subtract(1, 'day').startOf('week').unix())}`
|
||||
: undefined
|
||||
}
|
||||
feeAmount={SIMPLE_SITE ? !dynamicRouteProps && CS.FEE_AMOUNT_ANY : undefined}
|
||||
channelIds={
|
||||
(dynamicRouteProps && dynamicRouteProps.options && dynamicRouteProps.options.channelIds) || undefined
|
||||
}
|
||||
limitClaimsPerChannel={
|
||||
SIMPLE_SITE
|
||||
? (dynamicRouteProps && dynamicRouteProps.options && dynamicRouteProps.options.limitClaimsPerChannel) ||
|
||||
undefined
|
||||
: 3
|
||||
}
|
||||
meta={
|
||||
!dynamicRouteProps ? (
|
||||
<a
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// @flow
|
||||
import * as PAGES from 'constants/pages';
|
||||
import * as React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { lazyImport } from 'util/lazyImport';
|
||||
|
@ -11,6 +12,9 @@ import FileRenderDownload from 'component/fileRenderDownload';
|
|||
import RecommendedContent from 'component/recommendedContent';
|
||||
import CollectionContent from 'component/collectionContentSidebar';
|
||||
import CommentsList from 'component/commentsList';
|
||||
import { Redirect } from 'react-router';
|
||||
import Button from 'component/button';
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
import Empty from 'component/common/empty';
|
||||
|
||||
const PostViewer = lazyImport(() => import('component/postViewer' /* webpackChunkName: "postViewer" */));
|
||||
|
@ -32,7 +36,9 @@ type Props = {
|
|||
collection?: Collection,
|
||||
collectionId: string,
|
||||
videoTheaterMode: boolean,
|
||||
claimIsMine: boolean,
|
||||
commentsDisabled: boolean,
|
||||
isLivestream: boolean,
|
||||
};
|
||||
|
||||
function FilePage(props: Props) {
|
||||
|
@ -49,9 +55,12 @@ function FilePage(props: Props) {
|
|||
linkedCommentId,
|
||||
setPrimaryUri,
|
||||
videoTheaterMode,
|
||||
|
||||
claimIsMine,
|
||||
commentsDisabled,
|
||||
collection,
|
||||
collectionId,
|
||||
isLivestream,
|
||||
} = props;
|
||||
const cost = costInfo ? costInfo.cost : null;
|
||||
const hasFileInfo = fileInfo !== undefined;
|
||||
|
@ -136,6 +145,10 @@ function FilePage(props: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
if (!claimIsMine && isLivestream) {
|
||||
return <Redirect to={`/$/${PAGES.LIVESTREAM}`} />;
|
||||
}
|
||||
|
||||
if (obscureNsfw && isMature) {
|
||||
return (
|
||||
<Page className="file-page" filePage isMarkdown={isMarkdown}>
|
||||
|
@ -156,6 +169,18 @@ function FilePage(props: Props) {
|
|||
{!isMarkdown && (
|
||||
<div className="file-page__secondary-content">
|
||||
<div>
|
||||
{claimIsMine && isLivestream && (
|
||||
<div className="livestream__creator-message">
|
||||
<h4>{__('Only visible to you')}</h4>
|
||||
<I18nMessage>
|
||||
People who view this link will be redirected to your livestream. Make sure to use this for sharing
|
||||
so your title and thumbnail are displayed properly.
|
||||
</I18nMessage>
|
||||
<div className="section__actions">
|
||||
<Button button="primary" navigate={`/$/${PAGES.LIVESTREAM}`} label={__('View livestream')} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitleSection uri={uri} />}
|
||||
{commentsDisabled && <Empty text={__('The creator of this content has disabled comments.')} />}
|
||||
{!commentsDisabled && <CommentsList uri={uri} linkedCommentId={linkedCommentId} />}
|
||||
|
|
|
@ -62,7 +62,6 @@ function HomePage(props: Props) {
|
|||
showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS}
|
||||
hasSource
|
||||
pinUrls={pinUrls}
|
||||
pin={route === `/$/${PAGES.GENERAL}`} // use pinUrls here
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -145,7 +144,7 @@ function HomePage(props: Props) {
|
|||
{/* @if TARGET='web' */}
|
||||
{SIMPLE_SITE && <Meme />}
|
||||
{/* @endif */}
|
||||
{rowData.map(({ title, route, link, icon, help, pinUrls, options = {} }, index) => {
|
||||
{rowData.map(({ title, route, link, icon, help, pinnedUrls: pinUrls, options = {} }, index) => {
|
||||
// add pins here
|
||||
return getRowElements(title, route, link, icon, help, options, index, pinUrls);
|
||||
})}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import { BITWAVE_LIVE_API } from 'constants/livestream';
|
||||
import { LIVESTREAM_LIVE_API } from 'constants/livestream';
|
||||
import React from 'react';
|
||||
import Page from 'component/page';
|
||||
import LivestreamLayout from 'component/livestreamLayout';
|
||||
|
@ -56,8 +56,9 @@ export default function LivestreamPage(props: Props) {
|
|||
React.useEffect(() => {
|
||||
let interval;
|
||||
function checkIsLive() {
|
||||
// $FlowFixMe Bitwave's API can handle garbage
|
||||
fetch(`${BITWAVE_LIVE_API}/${livestreamChannelId}`)
|
||||
// TODO: duplicate code below
|
||||
// $FlowFixMe livestream API can handle garbage
|
||||
fetch(`${LIVESTREAM_LIVE_API}/${livestreamChannelId}`)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (!res || !res.data) {
|
||||
|
@ -105,7 +106,7 @@ export default function LivestreamPage(props: Props) {
|
|||
|
||||
React.useEffect(() => {
|
||||
// Set playing uri to null so the popout player doesnt start playing the dummy claim if a user navigates back
|
||||
// This can be removed when we start using the app video player, not a bitwave iframe
|
||||
// This can be removed when we start using the app video player, not a LIVESTREAM iframe
|
||||
doSetPlayingUri({ uri: null });
|
||||
}, [doSetPlayingUri]);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import CopyableText from 'component/copyableText';
|
|||
import Card from 'component/common/card';
|
||||
import ClaimList from 'component/claimList';
|
||||
import usePersistedState from 'effects/use-persisted-state';
|
||||
import { LIVESTREAM_RTMP_URL } from 'constants/livestream';
|
||||
|
||||
type Props = {
|
||||
channels: Array<ChannelClaim>,
|
||||
|
@ -182,7 +183,7 @@ export default function LivestreamSetupPage(props: Props) {
|
|||
primaryButton
|
||||
name="stream-server"
|
||||
label={__('Stream server')}
|
||||
copyable="rtmp://stream.odysee.com/live"
|
||||
copyable={LIVESTREAM_RTMP_URL}
|
||||
snackMessage={__('Copied')}
|
||||
/>
|
||||
<CopyableText
|
||||
|
|
|
@ -15,6 +15,7 @@ import Card from 'component/common/card';
|
|||
import SettingAccountPassword from 'component/settingAccountPassword';
|
||||
import classnames from 'classnames';
|
||||
import { getPasswordFromCookie } from 'util/saved-passwords';
|
||||
import { SIMPLE_SITE } from 'config';
|
||||
// $FlowFixMe
|
||||
import homepages from 'homepages';
|
||||
import { Lbryio } from 'lbryinc';
|
||||
|
@ -208,7 +209,8 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
className="card-stack"
|
||||
>
|
||||
{/* @if TARGET='web' */}
|
||||
{user && user.fiat_enabled && <Card
|
||||
{user && user.fiat_enabled && (
|
||||
<Card
|
||||
title={__('Bank Accounts')}
|
||||
subtitle={__('Connect a bank account to receive tips and compensation in your local currency')}
|
||||
actions={
|
||||
|
@ -221,10 +223,12 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
/>
|
||||
</div>
|
||||
}
|
||||
/>}
|
||||
/>
|
||||
)}
|
||||
{/* @endif */}
|
||||
|
||||
{/* @if TARGET='web' */}
|
||||
{isAuthenticated && (
|
||||
<Card
|
||||
title={__('Payment Methods')}
|
||||
subtitle={__('Add a credit card to tip creators in their local currency')}
|
||||
|
@ -239,6 +243,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
</div>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{/* @endif */}
|
||||
|
||||
<Card title={__('Language')} actions={<SettingLanguage />} />
|
||||
|
@ -393,7 +398,8 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
'Autoplay video and audio files when navigating to a file, as well as the next related item when a file finishes playing.'
|
||||
)}
|
||||
/>
|
||||
|
||||
{!SIMPLE_SITE && (
|
||||
<>
|
||||
<FormField
|
||||
type="checkbox"
|
||||
name="hide_reposts"
|
||||
|
@ -407,17 +413,21 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
}}
|
||||
checked={hideReposts}
|
||||
label={__('Hide reposts')}
|
||||
helper={__('You will not see reposts by people you follow or receive email notifying about them.')}
|
||||
helper={__(
|
||||
'You will not see reposts by people you follow or receive email notifying about them.'
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* <FormField
|
||||
{/*
|
||||
<FormField
|
||||
type="checkbox"
|
||||
name="show_anonymous"
|
||||
onChange={() => setClientSetting(SETTINGS.SHOW_ANONYMOUS, !showAnonymous)}
|
||||
checked={showAnonymous}
|
||||
label={__('Show anonymous content')}
|
||||
helper={__('Anonymous content is published without a channel.')}
|
||||
/> */}
|
||||
/>
|
||||
*/}
|
||||
|
||||
<FormField
|
||||
type="checkbox"
|
||||
|
@ -433,6 +443,8 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
'Mature content may include nudity, intense sexuality, profanity, or other adult content. By displaying mature content, you are affirming you are of legal age to view mature content in your country or jurisdiction. '
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -380,7 +380,7 @@ export default function SettingsCreatorPage(props: Props) {
|
|||
)}
|
||||
<TagsSearch
|
||||
label={__('Moderators')}
|
||||
labelAddNew={__('Add moderators')}
|
||||
labelAddNew={__('Add moderator')}
|
||||
onRemove={removeModerator}
|
||||
onSelect={addModerator}
|
||||
tagsPassedIn={moderatorTags}
|
||||
|
|
|
@ -163,7 +163,7 @@ class StripeAccountConnection extends React.Component<Props, State> {
|
|||
// if it's beamer's error indicating the account is not linked yet
|
||||
if (error.message.indexOf(errorString) > -1) {
|
||||
// get stripe link and set it on the frontend
|
||||
getAndSetAccountLink();
|
||||
getAndSetAccountLink(true);
|
||||
} else {
|
||||
// not an error from Beamer, throw it
|
||||
throw new Error(error);
|
||||
|
|
|
@ -436,7 +436,6 @@ export function doCommentCreate(
|
|||
...(environment ? { environment } : {}), // add environment for stripe if it exists
|
||||
})
|
||||
.then((result: CommentCreateResponse) => {
|
||||
console.log(result);
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_CREATE_COMPLETED,
|
||||
data: {
|
||||
|
@ -449,7 +448,6 @@ export function doCommentCreate(
|
|||
return result;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_CREATE_FAILED,
|
||||
data: error,
|
||||
|
@ -472,6 +470,9 @@ export function doCommentCreate(
|
|||
case 'comments are disabled by the creator':
|
||||
toastMessage = __('Unable to comment. The content owner has disabled comments.');
|
||||
break;
|
||||
case 'duplicate comment!':
|
||||
toastMessage = __('Please do not spam.');
|
||||
break;
|
||||
default:
|
||||
const BLOCKED_WORDS_ERR_MSG = 'the comment contents are blocked by';
|
||||
const SLOW_MODE_PARTIAL_ERR_MSG = 'Slow mode is on. Please wait at most';
|
||||
|
@ -1390,7 +1391,9 @@ export const doFetchCreatorSettings = (channelClaimIds: Array<string> = []) => {
|
|||
const channelId = channelSignatures[i].claim_id;
|
||||
settingsByChannelId[channelId] = settings[i];
|
||||
|
||||
settingsByChannelId[channelId].words = settingsByChannelId[channelId].words.split(',');
|
||||
if (settings[i].words) {
|
||||
settingsByChannelId[channelId].words = settings[i].words.split(',');
|
||||
}
|
||||
|
||||
delete settingsByChannelId[channelId].channel_name;
|
||||
delete settingsByChannelId[channelId].channel_id;
|
||||
|
|
|
@ -986,6 +986,11 @@ export default handleActions(
|
|||
fetchingSettings: false,
|
||||
}),
|
||||
[ACTIONS.COMMENT_FETCH_SETTINGS_COMPLETED]: (state: CommentsState, action: any) => {
|
||||
// TODO: This is incorrect, as it could make 'settingsByChannelId' store
|
||||
// only 1 channel with other channel's data purged. It works for now
|
||||
// because the GUI only shows 1 channel's setting at a time, and *always*
|
||||
// re-fetches to get latest data before displaying. Either rename this to
|
||||
// 'activeChannelCreatorSettings', or append the new data properly.
|
||||
return {
|
||||
...state,
|
||||
settingsByChannelId: action.data,
|
||||
|
|
|
@ -3,17 +3,19 @@ import * as ACTIONS from 'constants/action_types';
|
|||
import { handleActions } from 'util/redux-utils';
|
||||
import { SEARCH_OPTIONS, SEARCH_PAGE_SIZE } from 'constants/search';
|
||||
import { createNormalizedSearchKey } from 'util/search';
|
||||
import { LIGHTHOUSE_DEFAULT_TYPES } from 'config';
|
||||
const defaultSearchTypes = LIGHTHOUSE_DEFAULT_TYPES && LIGHTHOUSE_DEFAULT_TYPES.split(',');
|
||||
|
||||
const defaultState: SearchState = {
|
||||
// $FlowFixMe
|
||||
options: {
|
||||
[SEARCH_OPTIONS.RESULT_COUNT]: SEARCH_PAGE_SIZE,
|
||||
[SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS,
|
||||
[SEARCH_OPTIONS.MEDIA_AUDIO]: true,
|
||||
[SEARCH_OPTIONS.MEDIA_VIDEO]: true,
|
||||
[SEARCH_OPTIONS.MEDIA_TEXT]: true,
|
||||
[SEARCH_OPTIONS.MEDIA_IMAGE]: true,
|
||||
[SEARCH_OPTIONS.MEDIA_APPLICATION]: true,
|
||||
[SEARCH_OPTIONS.MEDIA_AUDIO]: defaultSearchTypes.includes(SEARCH_OPTIONS.MEDIA_AUDIO),
|
||||
[SEARCH_OPTIONS.MEDIA_VIDEO]: defaultSearchTypes.includes(SEARCH_OPTIONS.MEDIA_VIDEO),
|
||||
[SEARCH_OPTIONS.MEDIA_TEXT]: defaultSearchTypes.includes(SEARCH_OPTIONS.MEDIA_TEXT),
|
||||
[SEARCH_OPTIONS.MEDIA_IMAGE]: defaultSearchTypes.includes(SEARCH_OPTIONS.MEDIA_IMAGE),
|
||||
[SEARCH_OPTIONS.MEDIA_APPLICATION]: defaultSearchTypes.includes(SEARCH_OPTIONS.MEDIA_APPLICATION),
|
||||
},
|
||||
urisByQuery: {},
|
||||
hasReachedMaxResultsLength: {},
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
min-width: 15rem;
|
||||
}
|
||||
|
||||
.ads__injected-video {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.avp-p-gui {
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
|
|
@ -105,6 +105,10 @@
|
|||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.file-render--post-container {
|
||||
min-height: 30vh;
|
||||
}
|
||||
|
||||
.file-render__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
|
@ -77,7 +77,6 @@
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-text);
|
||||
position: relative;
|
||||
font-weight: var(--font-weight-bold);
|
||||
|
||||
|
@ -151,6 +150,7 @@
|
|||
align-items: center;
|
||||
margin-left: var(--spacing-m);
|
||||
margin-right: var(--spacing-m);
|
||||
color: var(--color-text);
|
||||
// move to lbry theme?
|
||||
.lbry-icon {
|
||||
height: var(--height-button);
|
||||
|
|
|
@ -95,9 +95,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.file-page__post-comments {
|
||||
margin-top: var(--spacing-l);
|
||||
|
||||
opacity: 0;
|
||||
animation: fadeIn 2s;
|
||||
animation-delay: 2s;
|
||||
animation-fill-mode: forwards;
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
padding: var(--spacing-m);
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ export const getHomepageRowForCat = (cat: HomepageCat) => {
|
|||
route: cat.name ? `/$/${cat.name}` : undefined,
|
||||
icon: cat.icon || '', // some default
|
||||
title: cat.label,
|
||||
pinnedUrls: cat.pinnedUrls,
|
||||
options: {
|
||||
claimType: cat.claimType || 'stream',
|
||||
channelIds: cat.channelIds,
|
||||
|
@ -336,6 +337,9 @@ export function GetLinksData(
|
|||
rowData.push(LATEST_FROM_LBRY);
|
||||
if (!showPersonalizedChannels) rowData.push(TOP_CHANNELS);
|
||||
}
|
||||
(Object.values(all): any).map((row) => rowData.push(getHomepageRowForCat(row)));
|
||||
// TODO: provide better method for exempting from homepage
|
||||
(Object.values(all): any)
|
||||
.filter((row) => !(isHomepage && row.name === 'news'))
|
||||
.map((row) => rowData.push(getHomepageRowForCat(row)));
|
||||
return rowData;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,30 @@
|
|||
import React from 'react';
|
||||
|
||||
let localStorageAvailable;
|
||||
try {
|
||||
localStorageAvailable = Boolean(window.localStorage);
|
||||
} catch (e) {
|
||||
localStorageAvailable = false;
|
||||
}
|
||||
|
||||
export const lazyImport = (componentImport) =>
|
||||
React.lazy(async () => {
|
||||
const pageHasAlreadyBeenForceRefreshed = JSON.parse(
|
||||
window.localStorage.getItem('page-has-been-force-refreshed') || 'false'
|
||||
);
|
||||
const pageHasAlreadyBeenForceRefreshed = localStorageAvailable
|
||||
? JSON.parse(window.localStorage.getItem('page-has-been-force-refreshed') || 'false')
|
||||
: false;
|
||||
|
||||
try {
|
||||
const component = await componentImport();
|
||||
if (localStorageAvailable) {
|
||||
window.localStorage.setItem('page-has-been-force-refreshed', 'false');
|
||||
}
|
||||
return component;
|
||||
} catch (error) {
|
||||
if (!pageHasAlreadyBeenForceRefreshed) {
|
||||
// It's highly likely that the user's session is old. Try reloading once.
|
||||
if (localStorageAvailable) {
|
||||
window.localStorage.setItem('page-has-been-force-refreshed', 'true');
|
||||
}
|
||||
return window.location.reload();
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ function Ads(props: Props) {
|
|||
|
||||
const videoAd = (
|
||||
<div className="ads__claim-item">
|
||||
<div id="62d1eb10-e362-4873-99ed-c64a4052b43b" />
|
||||
<div id="62d1eb10-e362-4873-99ed-c64a4052b43b" className="ads__injected-video" />
|
||||
<div
|
||||
className={classnames('ads__claim-text', {
|
||||
'ads__claim-text--small': small,
|
||||
|
|
|
@ -4,7 +4,7 @@ import Button from 'component/button';
|
|||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { withRouter } from 'react-router';
|
||||
import { URL, SITE_NAME } from 'config';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import Logo from 'component/logo';
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
|
@ -37,7 +37,9 @@ function FileViewerEmbeddedEnded(props: Props) {
|
|||
return (
|
||||
<div className="file-viewer__overlay">
|
||||
<div className="file-viewer__overlay-secondary">
|
||||
<Button className="file-viewer__overlay-logo" label="LBRY" icon={ICONS.LBRY} href={URL} />
|
||||
<Button className="file-viewer__overlay-logo" href={URL}>
|
||||
<Logo type={'embed'} />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="file-viewer__overlay-title">{prompt}</div>
|
||||
<div className="file-viewer__overlay-actions">
|
||||
|
|
|
@ -1,101 +1,38 @@
|
|||
import * as PAGES from 'constants/pages';
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
|
||||
const sections = [
|
||||
{
|
||||
name: 'Community',
|
||||
links: [
|
||||
{
|
||||
label: 'Twitter',
|
||||
link: 'https://twitter.com/lbrycom',
|
||||
},
|
||||
{
|
||||
label: 'Reddit',
|
||||
link: 'https://reddit.com/r/lbry',
|
||||
},
|
||||
{
|
||||
label: 'Chat (Discord)',
|
||||
link: 'https://chat.lbry.org/',
|
||||
},
|
||||
{
|
||||
label: 'Telegram',
|
||||
link: 'https://t.me/lbryofficial',
|
||||
},
|
||||
{
|
||||
label: 'Facebook',
|
||||
link: 'https://www.facebook.com/lbryio',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Resources',
|
||||
links: [
|
||||
{
|
||||
label: 'FAQ',
|
||||
link: 'https://lbry.com/faq',
|
||||
},
|
||||
{
|
||||
label: 'Support --[used in footer; general help/support]--',
|
||||
link: 'https://lbry.com/faq/support',
|
||||
},
|
||||
{
|
||||
label: 'YouTube Partner Program',
|
||||
link: 'https://lbry.com/youtube',
|
||||
},
|
||||
{
|
||||
label: 'lbry.com',
|
||||
link: 'https://lbry.com',
|
||||
},
|
||||
{
|
||||
label: 'lbry.tech',
|
||||
link: 'https://lbry.tech',
|
||||
},
|
||||
{
|
||||
label: 'GitHub',
|
||||
link: 'https://github.com/lbryio',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Policies',
|
||||
links: [
|
||||
{
|
||||
label: 'Terms of Service',
|
||||
link: 'https://www.lbry.com/termsofservice',
|
||||
},
|
||||
{
|
||||
label: 'Privacy Policy',
|
||||
link: 'https://lbry.com/privacypolicy',
|
||||
},
|
||||
{
|
||||
label: '2257',
|
||||
navigate: `/$/${PAGES.CODE_2257}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
import { SIMPLE_SITE } from 'config';
|
||||
|
||||
export default function Footer() {
|
||||
if (!SIMPLE_SITE) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<footer className="footer">
|
||||
<ul className="navigation__tertiary footer__links ul--no-style">
|
||||
{sections.map(({ name, links }) => {
|
||||
return (
|
||||
<li key={name} className="footer__section">
|
||||
<ul className="ul--no-style">
|
||||
<div className="footer__section-title">{__(name)}</div>
|
||||
{links.map(({ label, link, navigate }) => {
|
||||
return (
|
||||
<li key={label}>
|
||||
<Button className="footer__link" label={__(label)} href={link} navigate={navigate} />
|
||||
<span className="footer__section-title">
|
||||
<I18nMessage tokens={{ lbry_link: <Button button="link" label={'LBRY'} href="https://lbry.com" /> }}>
|
||||
POWERED BY %lbry_link%
|
||||
</I18nMessage>
|
||||
</span>
|
||||
<ul className="navigation__tertiary footer__links">
|
||||
<li className="footer__link">
|
||||
<Button label={__('About --[link title in Sidebar or Footer]--')} href="https://lbry.com/about" />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<li className="footer__link">
|
||||
<Button label={__('Community Guidelines')} href="https://odysee.com/@OdyseeHelp:b/Community-Guidelines:c" />
|
||||
</li>
|
||||
<li className="footer__link">
|
||||
<Button label={__('FAQ')} href="https://odysee.com/@OdyseeHelp:b" />
|
||||
</li>
|
||||
<li className="footer__link">
|
||||
<Button label={__('Support --[used in footer; general help/support]--')} href="https://lbry.com/support" />
|
||||
</li>
|
||||
<li className="footer__link">
|
||||
<Button label={__('Terms')} href="https://lbry.com/termsofservice" />
|
||||
</li>
|
||||
<li className="footer__link">
|
||||
<Button label={__('Privacy Policy')} href="https://lbry.com/privacy" />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</footer>
|
||||
);
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
const memes = require('memes');
|
||||
|
||||
export default function Meme() {
|
||||
return (
|
||||
<h1 className="home__meme">
|
||||
<Button button="link" href="https://odysee.com/@Odysee:8?view=discussion">
|
||||
{'big gulps, huh?'}
|
||||
<Button button="link" href={memes.url}>
|
||||
{memes.text}
|
||||
</Button>
|
||||
</h1>
|
||||
);
|
||||
|
|
|
@ -48,11 +48,11 @@ module.exports.CATEGORY_METADATA = {
|
|||
description: `Do you love B rated movies? We've got you covered on Odysee`,
|
||||
image: 'https://spee.ch/category-movies:2.jpg?quality=80&height=1200&width=630',
|
||||
},
|
||||
// [PAGES.MUSIC]: {
|
||||
// title: 'Music',
|
||||
// description: 'All the music you love on Odysee',
|
||||
// image: 'https://spee.ch/category-music:8.jpg?quality=80&height=1200&width=630',
|
||||
// },
|
||||
[PAGES.MUSIC]: {
|
||||
title: 'Music',
|
||||
description: 'All the songs, reviews, covers, and how-tos you love on Odysee',
|
||||
image: 'https://spee.ch/category-music:8.jpg?quality=80&height=1200&width=630',
|
||||
},
|
||||
[PAGES.TECHNOLOGY]: {
|
||||
title: 'Tech',
|
||||
description: 'Hardware, software, startups, photography on Odysee',
|
||||
|
|
|
@ -8,6 +8,7 @@ const {
|
|||
OG_IMAGE_URL,
|
||||
SITE_DESCRIPTION,
|
||||
SITE_NAME,
|
||||
FAVICON,
|
||||
} = require('../../config.js');
|
||||
const { generateEmbedUrl, generateStreamUrl, generateDirectUrl } = require('../../ui/util/web');
|
||||
const PAGES = require('../../ui/constants/pages');
|
||||
|
@ -89,13 +90,24 @@ function conditionallyAddPWA() {
|
|||
return head;
|
||||
}
|
||||
|
||||
function addFavicon() {
|
||||
let head = '';
|
||||
head += `<link rel="icon" type="image/png" href="${FAVICON || './public/favicon.png'}" />`;
|
||||
return head;
|
||||
}
|
||||
|
||||
function buildHead() {
|
||||
const head = '<!-- VARIABLE_HEAD_BEGIN -->' + conditionallyAddPWA() + buildOgMetadata() + '<!-- VARIABLE_HEAD_END -->';
|
||||
const head =
|
||||
'<!-- VARIABLE_HEAD_BEGIN -->' +
|
||||
addFavicon() +
|
||||
conditionallyAddPWA() +
|
||||
buildOgMetadata() +
|
||||
'<!-- VARIABLE_HEAD_END -->';
|
||||
return head;
|
||||
}
|
||||
|
||||
function buildBasicOgMetadata() {
|
||||
const head = '<!-- VARIABLE_HEAD_BEGIN -->' + buildOgMetadata() + '<!-- VARIABLE_HEAD_END -->';
|
||||
const head = '<!-- VARIABLE_HEAD_BEGIN -->' + addFavicon() + buildOgMetadata() + '<!-- VARIABLE_HEAD_END -->';
|
||||
return head;
|
||||
}
|
||||
|
||||
|
@ -128,6 +140,7 @@ function buildClaimOgMetadata(uri, claim, overrideOptions = {}) {
|
|||
|
||||
let head = '';
|
||||
|
||||
head += `${addFavicon()}`;
|
||||
head += '<meta charset="utf8"/>';
|
||||
head += `<title>${title}</title>`;
|
||||
head += `<meta name="description" content="${cleanDescription}"/>`;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const { generateDownloadUrl } = require('../../ui/util/web');
|
||||
const { URL, SITE_NAME, LBRY_WEB_API } = require('../../config.js');
|
||||
const { URL, SITE_NAME, LBRY_WEB_API, FAVICON } = require('../../config.js');
|
||||
const { Lbry } = require('lbry-redux');
|
||||
const Feed = require('feed').Feed;
|
||||
|
||||
|
@ -82,7 +82,7 @@ async function getFeed(channelClaim, feedLink) {
|
|||
const title = value ? value.title : channelClaim.name;
|
||||
|
||||
const options = {
|
||||
favicon: URL + '/public/favicon.png',
|
||||
favicon: FAVICON || URL + '/public/favicon.png',
|
||||
generator: SITE_NAME + ' RSS Feed',
|
||||
title: title + ' on ' + SITE_NAME,
|
||||
description: fmtDescription(value && value.description ? value.description : ''),
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
const { URL, SITE_TITLE } = require('../../config.js');
|
||||
|
||||
const { URL, SITE_TITLE, FAVICON } = require('../../config.js');
|
||||
const favicon = FAVICON || `${URL}/public/favicon.png`;
|
||||
function getOpenSearchXml() {
|
||||
return (
|
||||
`<ShortName>${SITE_TITLE}</ShortName>` +
|
||||
`<Description>Search ${SITE_TITLE}</Description>` +
|
||||
'<InputEncoding>UTF-8</InputEncoding>' +
|
||||
`<Image width="32" height="32" type="image/png">${URL}/public/favicon.png</Image>` +
|
||||
`<Image width="32" height="32" type="image/png">${favicon}</Image>` +
|
||||
`<Url type="text/html" method="get" template="${URL}/$/search?q={searchTerms}"/>` +
|
||||
`<moz:SearchForm>${URL}</moz:SearchForm>`
|
||||
);
|
||||
|
|
|
@ -77,6 +77,7 @@ let baseConfig = {
|
|||
config: path.resolve(__dirname, 'config.js'),
|
||||
homepage: 'util/homepage.js',
|
||||
homepages: process.env.CUSTOM_HOMEPAGE === 'true' ? path.resolve(__dirname, 'custom/homepages/v2/index.js') : ('homepages/index.js'),
|
||||
memes: process.env.CUSTOM_HOMEPAGE === 'true' ? path.resolve(__dirname, 'custom/homepages/meme/index.js') : path.resolve(__dirname, 'homepages/meme/index.js'),
|
||||
lbryinc: 'lbryinc/dist/bundle.es.js',
|
||||
// Build optimizations for 'redux-persist-transform-filter'
|
||||
'redux-persist-transform-filter': 'redux-persist-transform-filter/index.js',
|
||||
|
|
Loading…
Reference in a new issue