Merge branch 'master' into protocol
This commit is contained in:
commit
3f3be5feb0
104 changed files with 4334 additions and 464 deletions
|
@ -11,7 +11,7 @@ WEB_SERVER_PORT=1337
|
|||
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
|
||||
COMMENT_SERVER_API=https://comments.odysee.com/api/v2
|
||||
THUMBNAIL_CDN_URL=https://image-processor.vanwanet.com/optimize/
|
||||
WELCOME_VERSION=1.0
|
||||
|
||||
|
@ -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
|
||||
|
@ -67,6 +68,7 @@ AUTO_FOLLOW_CHANNELS=lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a
|
|||
|
||||
## FEATURES AND LIMITS
|
||||
SIMPLE_SITE=false
|
||||
#BRANDED_SITE
|
||||
|
||||
ENABLE_COMMENT_REACTIONS=true
|
||||
ENABLE_FILE_REACTIONS=false
|
||||
|
@ -77,6 +79,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
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"ui/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"],
|
||||
"web/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"],
|
||||
"ui/**/*.{js,jsx}": ["eslint", "flow focus-check --color always", "git add"],
|
||||
"web/**/*.{js,jsx,scss}": ["eslint", "git add"]
|
||||
"web/**/*.{js,jsx}": ["eslint", "git add"]
|
||||
},
|
||||
"ignore": ["node_modules", "web/dist/**/*", "dist/**/*", "package-lock.json"]
|
||||
}
|
||||
|
|
|
@ -8,17 +8,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
### Added
|
||||
- Show currently active playing item on playlist _community pr!_ ([#6453](https://github.com/lbryio/lbry-desktop/pull/6453))
|
||||
- Add watch later to hover action for last used playlist on popup _community pr!_ ([#6274](https://github.com/lbryio/lbry-desktop/pull/6274))
|
||||
- Open in desktop (web feature) _community pr!_ ([#6667](https://github.com/lbryio/lbry-desktop/pull/6667))
|
||||
|
||||
### Changed
|
||||
- Use Canonical Url for copy link ([#6500](https://github.com/lbryio/lbry-desktop/pull/6500))
|
||||
- Use better icon for copy link ([#6485](https://github.com/lbryio/lbry-desktop/pull/6485))
|
||||
- Comments load paginated ([#6390](https://github.com/lbryio/lbry-desktop/pull/6390))
|
||||
- Update lighthouse search api _community pr!_ ([#6731](https://github.com/lbryio/lbry-desktop/pull/6731))
|
||||
- Improve twitter share _community pr!_ ([#6690](https://github.com/lbryio/lbry-desktop/pull/6690))
|
||||
|
||||
### Fixed
|
||||
- App now supports '#' and ':' for claimId separator ([#6496](https://github.com/lbryio/lbry-desktop/pull/6496))
|
||||
- Fix "exact match" being applied to Recommended ([#6460](https://github.com/lbryio/lbry-desktop/pull/6460))
|
||||
- Fix upload button on creator analytics _community pr!_ ([#6458](https://github.com/lbryio/lbry-desktop/pull/6458))
|
||||
- Prevent sidebar shortcut activation on textarea _community pr!_ ([#6454](https://github.com/lbryio/lbry-desktop/pull/6454))
|
||||
- Improve accessibility and some minor css fixes _community pr!_ ([#6470](https:/github.com/lbryio/lbry-desktop/pull/6470))
|
||||
- Fix drag / drop publish issues for web users _community pr!_ ([#6466](https://github.com/lbryio/lbry-desktop/pull/6466))
|
||||
- Fix yarn copyenv on windows _community pr!_ ([#6702](https://github.com/lbryio/lbry-desktop/pull/6702))
|
||||
- Fix unnecessary livestream api calls in channel page _community pr!_ ([#6652](https://github.com/lbryio/lbry-desktop/pull/6652))
|
||||
|
||||
## [0.51.1] - [2021-06-26]
|
||||
|
||||
|
|
11
config.js
11
config.js
|
@ -9,6 +9,7 @@ const config = {
|
|||
WEBPACK_ELECTRON_PORT: process.env.WEBPACK_ELECTRON_PORT,
|
||||
WEB_SERVER_PORT: process.env.WEB_SERVER_PORT,
|
||||
LBRY_WEB_API: process.env.LBRY_WEB_API, //api.na-backend.odysee.com',
|
||||
LBRY_WEB_PUBLISH_API: process.env.LBRY_WEB_PUBLISH_API,
|
||||
LBRY_API_URL: process.env.LBRY_API_URL, //api.lbry.com',
|
||||
LBRY_WEB_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //cdn.lbryplayer.xyz',
|
||||
LBRY_WEB_BUFFER_API: process.env.LBRY_WEB_BUFFER_API,
|
||||
|
@ -25,9 +26,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,
|
||||
|
@ -63,7 +65,8 @@ 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',
|
||||
AVATAR_DEFAULT: process.env.AVATAR_DEFAULT,
|
||||
LIGHTHOUSE_DEFAULT_TYPES: process.env.LIGHTHOUSE_DEFAULT_TYPES,
|
||||
BRANDED_SITE: process.env.BRANDED_SITE,
|
||||
};
|
||||
|
||||
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',
|
||||
};
|
|
@ -24,7 +24,7 @@
|
|||
"compile:electron": "node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --config webpack.electron.config.js",
|
||||
"compile:web": "yarn copyenv && cd web && node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --config webpack.config.js",
|
||||
"compile": "cross-env NODE_ENV=production yarn compile:electron && cross-env NODE_ENV=production yarn compile:web",
|
||||
"copyenv": "cp ./.env* web/",
|
||||
"copyenv": "copyfiles ./.env* web/",
|
||||
"dev": "yarn dev:electron",
|
||||
"dev:electron": "cross-env NODE_ENV=development node ./electron/devServer.js",
|
||||
"dev:web": "yarn copyenv && cd web && yarn dev",
|
||||
|
@ -107,6 +107,7 @@
|
|||
"concurrently": "^4.1.2",
|
||||
"connected-react-router": "^6.8.0",
|
||||
"copy-webpack-plugin": "^5.1.2",
|
||||
"copyfiles": "^2.4.1",
|
||||
"country-data": "^0.0.31",
|
||||
"cross-env": "^7.0.3",
|
||||
"crypto-js": "^4.0.0",
|
||||
|
|
|
@ -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,12 +2056,20 @@
|
|||
"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",
|
||||
"added to": "added to",
|
||||
"removed from": "removed from",
|
||||
"Skip Navigation": "Skip Navigation",
|
||||
"Reset": "Reset",
|
||||
"Reset to original (previous) publish date": "Reset to original (previous) publish date",
|
||||
"Show reply": "Show reply",
|
||||
"Show %count% replies": "Show %count% replies",
|
||||
"Open in Desktop": "Open in Desktop",
|
||||
"%title% by %channelTitle%": "%title% by %channelTitle%",
|
||||
"%title% by %channelTitle% %ariaDate%": "%title% by %channelTitle% %ariaDate%",
|
||||
"%title% by %channelTitle% %ariaDate%, %mediaDuration%": "%title% by %channelTitle% %ariaDate%, %mediaDuration%",
|
||||
"Search for something...": "Search for something...",
|
||||
"--end--": "--end--"
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -285,6 +285,11 @@ function App(props: Props) {
|
|||
}
|
||||
}, [hasMyChannels, hasNoChannels, hasActiveChannelClaim, setActiveChannelIfNotSet, setIncognito]);
|
||||
|
||||
useEffect(() => {
|
||||
// $FlowFixMe
|
||||
document.documentElement.setAttribute('lang', language);
|
||||
}, [language]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!languages.includes(language)) {
|
||||
setLanguage(language);
|
||||
|
|
|
@ -97,6 +97,9 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
const combinedRef = useCombinedRefs(ref, innerRef, myref);
|
||||
const size = iconSize || (!label && !children) ? 18 : undefined; // Fall back to default
|
||||
|
||||
// Label can be a string or object ( use title instead )
|
||||
const ariaLabel = description || (typeof label === 'string' ? label : title);
|
||||
|
||||
const content = (
|
||||
<span className="button__content">
|
||||
{icon && <Icon icon={icon} iconColor={iconColor} size={iconSize} />}
|
||||
|
@ -150,6 +153,7 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
className={combinedClassName}
|
||||
title={title}
|
||||
onClick={onClick}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
|
@ -196,6 +200,7 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
disabled={disable}
|
||||
className={combinedClassName}
|
||||
activeClassName={activeClass}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
{content}
|
||||
</NavLink>
|
||||
|
@ -216,6 +221,7 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
}}
|
||||
className={combinedClassName}
|
||||
activeClassName={activeClass}
|
||||
aria-label={ariaLabel}
|
||||
{...otherProps}
|
||||
>
|
||||
{content}
|
||||
|
@ -224,7 +230,7 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
<button
|
||||
ref={combinedRef}
|
||||
title={title || defaultTooltip}
|
||||
aria-label={description || label || title}
|
||||
aria-label={ariaLabel}
|
||||
className={combinedClassName}
|
||||
onClick={(e) => {
|
||||
if (onClick) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' })}>
|
||||
|
|
|
@ -15,6 +15,8 @@ import {
|
|||
doCollectionEdit,
|
||||
makeSelectUrlsForCollectionId,
|
||||
makeSelectIndexForUrlInCollection,
|
||||
makeSelectTitleForUri,
|
||||
makeSelectDateForUri,
|
||||
} from 'lbry-redux';
|
||||
import { selectMutedChannels, makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||
|
@ -22,32 +24,43 @@ import { selectShowMatureContent } from 'redux/selectors/settings';
|
|||
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
||||
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
||||
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||
import ClaimPreview from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
pending: props.uri && makeSelectClaimIsPending(props.uri)(state),
|
||||
claim: props.uri && makeSelectClaimForUri(props.uri)(state),
|
||||
reflectingProgress: props.uri && makeSelectReflectingClaimForUri(props.uri)(state),
|
||||
obscureNsfw: selectShowMatureContent(state) === false,
|
||||
claimIsMine: props.uri && makeSelectClaimIsMine(props.uri)(state),
|
||||
isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state),
|
||||
isResolvingRepost: props.uri && makeSelectIsUriResolving(props.repostUrl)(state),
|
||||
repostClaim: props.uri && makeSelectClaimForUri(props.uri)(state),
|
||||
nsfw: props.uri && makeSelectClaimIsNsfw(props.uri)(state),
|
||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||
filteredOutpoints: selectFilteredOutpoints(state),
|
||||
mutedUris: selectMutedChannels(state),
|
||||
blockedUris: selectModerationBlockList(state),
|
||||
hasVisitedUri: props.uri && makeSelectHasVisitedUri(props.uri)(state),
|
||||
channelIsBlocked: props.uri && makeSelectChannelIsMuted(props.uri)(state),
|
||||
isSubscribed: props.uri && makeSelectIsSubscribed(props.uri, true)(state),
|
||||
streamingUrl: props.uri && makeSelectStreamingUrlForUri(props.uri)(state),
|
||||
wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state),
|
||||
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||
isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state),
|
||||
collectionUris: makeSelectUrlsForCollectionId(props.collectionId)(state),
|
||||
collectionIndex: makeSelectIndexForUrlInCollection(props.uri, props.collectionId)(state),
|
||||
});
|
||||
import ClaimPreview from './view';
|
||||
import formatMediaDuration from 'util/formatMediaDuration';
|
||||
|
||||
const select = (state, props) => {
|
||||
const claim = props.uri && makeSelectClaimForUri(props.uri)(state);
|
||||
const media = claim && claim.value && (claim.value.video || claim.value.audio);
|
||||
const mediaDuration = media && media.duration && formatMediaDuration(media.duration, { screenReader: true });
|
||||
|
||||
return {
|
||||
claim,
|
||||
mediaDuration,
|
||||
date: props.uri && makeSelectDateForUri(props.uri)(state),
|
||||
title: props.uri && makeSelectTitleForUri(props.uri)(state),
|
||||
pending: props.uri && makeSelectClaimIsPending(props.uri)(state),
|
||||
reflectingProgress: props.uri && makeSelectReflectingClaimForUri(props.uri)(state),
|
||||
obscureNsfw: selectShowMatureContent(state) === false,
|
||||
claimIsMine: props.uri && makeSelectClaimIsMine(props.uri)(state),
|
||||
isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state),
|
||||
isResolvingRepost: props.uri && makeSelectIsUriResolving(props.repostUrl)(state),
|
||||
repostClaim: props.uri && makeSelectClaimForUri(props.uri)(state),
|
||||
nsfw: props.uri && makeSelectClaimIsNsfw(props.uri)(state),
|
||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||
filteredOutpoints: selectFilteredOutpoints(state),
|
||||
mutedUris: selectMutedChannels(state),
|
||||
blockedUris: selectModerationBlockList(state),
|
||||
hasVisitedUri: props.uri && makeSelectHasVisitedUri(props.uri)(state),
|
||||
channelIsBlocked: props.uri && makeSelectChannelIsMuted(props.uri)(state),
|
||||
isSubscribed: props.uri && makeSelectIsSubscribed(props.uri, true)(state),
|
||||
streamingUrl: props.uri && makeSelectStreamingUrlForUri(props.uri)(state),
|
||||
wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state),
|
||||
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||
isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state),
|
||||
collectionUris: makeSelectUrlsForCollectionId(props.collectionId)(state),
|
||||
collectionIndex: makeSelectIndexForUrlInCollection(props.uri, props.collectionId)(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
resolveUri: (uri) => dispatch(doResolveUri(uri)),
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
import type { Node } from 'react';
|
||||
import React, { useEffect, forwardRef } from 'react';
|
||||
import { NavLink, withRouter } from 'react-router-dom';
|
||||
import { isEmpty } from 'util/object';
|
||||
import { lazyImport } from 'util/lazyImport';
|
||||
import classnames from 'classnames';
|
||||
import { parseURI, COLLECTIONS_CONSTS, isURIEqual } from 'lbry-redux';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { isEmpty } from 'util/object';
|
||||
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
|
||||
import FileThumbnail from 'component/fileThumbnail';
|
||||
import UriIndicator from 'component/uriIndicator';
|
||||
import PreviewOverlayProperties from 'component/previewOverlayProperties';
|
||||
|
@ -85,6 +86,8 @@ type Props = {
|
|||
collectionUris: Array<Collection>,
|
||||
collectionIndex?: number,
|
||||
disableNavigation?: boolean,
|
||||
mediaDuration?: string,
|
||||
date?: any,
|
||||
};
|
||||
|
||||
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||
|
@ -99,8 +102,11 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
// claim properties
|
||||
// is the claim consider nsfw?
|
||||
nsfw,
|
||||
date,
|
||||
title,
|
||||
claimIsMine,
|
||||
streamingUrl,
|
||||
mediaDuration,
|
||||
// user properties
|
||||
channelIsBlocked,
|
||||
hasVisitedUri,
|
||||
|
@ -175,6 +181,21 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
(claim.value.stream_type === 'audio' || claim.value.stream_type === 'video');
|
||||
const isChannelUri = isValid ? parseURI(uri).isChannel : false;
|
||||
const signingChannel = claim && claim.signing_channel;
|
||||
|
||||
// Get channel title ( use name as fallback )
|
||||
let channelTitle = null;
|
||||
if (signingChannel) {
|
||||
const { value, name } = signingChannel;
|
||||
if (value && value.title) {
|
||||
channelTitle = value.title;
|
||||
} else {
|
||||
channelTitle = name;
|
||||
}
|
||||
}
|
||||
|
||||
// Aria-label value for claim preview
|
||||
let ariaLabelData = isChannelUri ? title : formatClaimPreviewTitle(title, channelTitle, date, mediaDuration);
|
||||
|
||||
let navigateUrl = formatLbryUrlForWeb((claim && claim.canonical_url) || uri || '/');
|
||||
if (listId) {
|
||||
const collectionParams = new URLSearchParams();
|
||||
|
@ -313,18 +334,18 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
})}
|
||||
>
|
||||
{isChannelUri && claim ? (
|
||||
<UriIndicator uri={uri} link>
|
||||
<UriIndicator focusable={false} uri={uri} link>
|
||||
<ChannelThumbnail uri={uri} small={type === 'inline'} />
|
||||
</UriIndicator>
|
||||
) : (
|
||||
<>
|
||||
{!pending ? (
|
||||
<NavLink {...navLinkProps}>
|
||||
<NavLink aria-hidden tabIndex={-1} {...navLinkProps}>
|
||||
<FileThumbnail thumbnail={thumbnailUrl}>
|
||||
{/* @if TARGET='app' */}
|
||||
{claim && !isCollection && (
|
||||
<div className="claim-preview__hover-actions">
|
||||
<FileDownloadLink uri={canonicalUrl} hideOpenButton hideDownloadStatus />
|
||||
<FileDownloadLink focusable={false} uri={canonicalUrl} hideOpenButton hideDownloadStatus />
|
||||
</div>
|
||||
)}
|
||||
{/* @endif */}
|
||||
|
@ -335,7 +356,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
)}
|
||||
{isPlayable && (
|
||||
<div className="claim-preview__hover-actions">
|
||||
<FileWatchLaterLink uri={uri} />
|
||||
<FileWatchLaterLink focusable={false} uri={uri} />
|
||||
</div>
|
||||
)}
|
||||
</FileThumbnail>
|
||||
|
@ -352,7 +373,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
{pending ? (
|
||||
<ClaimPreviewTitle uri={uri} />
|
||||
) : (
|
||||
<NavLink aria-current={active && 'page'} {...navLinkProps}>
|
||||
<NavLink aria-label={ariaLabelData} aria-current={active ? 'page' : null} {...navLinkProps}>
|
||||
<ClaimPreviewTitle uri={uri} />
|
||||
</NavLink>
|
||||
)}
|
||||
|
@ -451,9 +472,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
{!hideMenu && (
|
||||
<ClaimMenuList uri={uri} collectionId={listId} />
|
||||
)}
|
||||
{!hideMenu && <ClaimMenuList uri={uri} collectionId={listId} />}
|
||||
</>
|
||||
</WrapperElement>
|
||||
);
|
||||
|
|
|
@ -9,25 +9,35 @@ import {
|
|||
makeSelectChannelForClaimUri,
|
||||
makeSelectClaimIsNsfw,
|
||||
makeSelectClaimIsStreamPlaceholder,
|
||||
makeSelectDateForUri,
|
||||
} from 'lbry-redux';
|
||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import ClaimPreviewTile from './view';
|
||||
import formatMediaDuration from 'util/formatMediaDuration';
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: props.uri && makeSelectClaimForUri(props.uri)(state),
|
||||
channel: props.uri && makeSelectChannelForClaimUri(props.uri)(state),
|
||||
isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state),
|
||||
thumbnail: props.uri && makeSelectThumbnailForUri(props.uri)(state),
|
||||
title: props.uri && makeSelectTitleForUri(props.uri)(state),
|
||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||
filteredOutpoints: selectFilteredOutpoints(state),
|
||||
blockedChannelUris: selectMutedChannels(state),
|
||||
showMature: selectShowMatureContent(state),
|
||||
isMature: makeSelectClaimIsNsfw(props.uri)(state),
|
||||
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||
});
|
||||
const select = (state, props) => {
|
||||
const claim = props.uri && makeSelectClaimForUri(props.uri)(state);
|
||||
const media = claim && claim.value && (claim.value.video || claim.value.audio);
|
||||
const mediaDuration = media && media.duration && formatMediaDuration(media.duration, { screenReader: true });
|
||||
|
||||
return {
|
||||
claim,
|
||||
mediaDuration,
|
||||
date: props.uri && makeSelectDateForUri(props.uri)(state),
|
||||
channel: props.uri && makeSelectChannelForClaimUri(props.uri)(state),
|
||||
isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state),
|
||||
thumbnail: props.uri && makeSelectThumbnailForUri(props.uri)(state),
|
||||
title: props.uri && makeSelectTitleForUri(props.uri)(state),
|
||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||
filteredOutpoints: selectFilteredOutpoints(state),
|
||||
blockedChannelUris: selectMutedChannels(state),
|
||||
showMature: selectShowMatureContent(state),
|
||||
isMature: makeSelectClaimIsNsfw(props.uri)(state),
|
||||
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
resolveUri: (uri) => dispatch(doResolveUri(uri)),
|
||||
|
|
|
@ -10,6 +10,7 @@ import ChannelThumbnail from 'component/channelThumbnail';
|
|||
import SubscribeButton from 'component/subscribeButton';
|
||||
import useGetThumbnail from 'effects/use-get-thumbnail';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
|
||||
import { parseURI, COLLECTIONS_CONSTS, isURIEqual } from 'lbry-redux';
|
||||
import PreviewOverlayProperties from 'component/previewOverlayProperties';
|
||||
import FileDownloadLink from 'component/fileDownloadLink';
|
||||
|
@ -22,7 +23,9 @@ import PlaceholderTx from 'static/img/placeholderTx.gif';
|
|||
|
||||
type Props = {
|
||||
uri: string,
|
||||
date?: any,
|
||||
claim: ?Claim,
|
||||
mediaDuration?: string,
|
||||
resolveUri: (string) => void,
|
||||
isResolvingUri: boolean,
|
||||
history: { push: (string) => void },
|
||||
|
@ -54,6 +57,7 @@ function ClaimPreviewTile(props: Props) {
|
|||
const {
|
||||
history,
|
||||
uri,
|
||||
date,
|
||||
isResolvingUri,
|
||||
thumbnail,
|
||||
title,
|
||||
|
@ -73,6 +77,7 @@ function ClaimPreviewTile(props: Props) {
|
|||
showNoSourceClaims,
|
||||
isLivestream,
|
||||
collectionId,
|
||||
mediaDuration,
|
||||
} = props;
|
||||
const isRepost = claim && claim.repost_channel_url;
|
||||
const isCollection = claim && claim.value_type === 'collection';
|
||||
|
@ -115,6 +120,10 @@ 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);
|
||||
|
||||
// Aria-label value for claim preview
|
||||
let ariaLabelData = isChannel ? title : formatClaimPreviewTitle(title, channelTitle, date, mediaDuration);
|
||||
|
||||
function handleClick(e) {
|
||||
if (navigateUrl) {
|
||||
|
@ -189,28 +198,27 @@ function ClaimPreviewTile(props: Props) {
|
|||
|
||||
return (
|
||||
<li
|
||||
role="link"
|
||||
onClick={handleClick}
|
||||
className={classnames('card claim-preview--tile', {
|
||||
'claim-preview__wrapper--channel': isChannel,
|
||||
'claim-preview__live': live,
|
||||
})}
|
||||
>
|
||||
<NavLink {...navLinkProps}>
|
||||
<NavLink {...navLinkProps} role="none" tabIndex={-1} aria-hidden>
|
||||
<FileThumbnail thumbnail={thumbnailUrl} allowGifs>
|
||||
{!isChannel && (
|
||||
<React.Fragment>
|
||||
{/* @if TARGET='app' */}
|
||||
{isStream && (
|
||||
<div className="claim-preview__hover-actions">
|
||||
<FileDownloadLink uri={canonicalUrl} hideOpenButton />
|
||||
<FileDownloadLink focusable={false} uri={canonicalUrl} hideOpenButton />
|
||||
</div>
|
||||
)}
|
||||
{/* @endif */}
|
||||
|
||||
{isPlayable && (
|
||||
<div className="claim-preview__hover-actions">
|
||||
<FileWatchLaterLink uri={uri} />
|
||||
<FileWatchLaterLink focusable={false} uri={uri} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -228,17 +236,19 @@ function ClaimPreviewTile(props: Props) {
|
|||
)}
|
||||
</FileThumbnail>
|
||||
</NavLink>
|
||||
<NavLink {...navLinkProps}>
|
||||
<h2 className="claim-tile__title">
|
||||
<TruncatedText text={title || (claim && claim.name)} lines={isChannel ? 1 : 2} />
|
||||
{isChannel && (
|
||||
<div className="claim-tile__about">
|
||||
<UriIndicator uri={uri} />
|
||||
</div>
|
||||
)}
|
||||
<ClaimMenuList uri={uri} collectionId={listId} />
|
||||
</h2>
|
||||
</NavLink>
|
||||
<div className="claim-tile__header">
|
||||
<NavLink aria-label={ariaLabelData} {...navLinkProps}>
|
||||
<h2 className="claim-tile__title">
|
||||
<TruncatedText text={title || (claim && claim.name)} lines={isChannel ? 1 : 2} />
|
||||
{isChannel && (
|
||||
<div className="claim-tile__about">
|
||||
<UriIndicator uri={uri} />
|
||||
</div>
|
||||
)}
|
||||
</h2>
|
||||
</NavLink>
|
||||
<ClaimMenuList uri={uri} collectionId={listId} channelUri={channelUri} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="claim-tile__info">
|
||||
{isChannel ? (
|
||||
|
@ -247,7 +257,7 @@ function ClaimPreviewTile(props: Props) {
|
|||
</div>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<UriIndicator uri={uri} link hideAnonymous>
|
||||
<UriIndicator focusable={false} uri={uri} link hideAnonymous>
|
||||
<ChannelThumbnail uri={channelUri} xsmall />
|
||||
</UriIndicator>
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -7,9 +7,14 @@ import {
|
|||
makeSelectClaimForUri,
|
||||
makeSelectClaimIsMine,
|
||||
} from 'lbry-redux';
|
||||
import {
|
||||
selectPlayingUri,
|
||||
} from 'redux/selectors/content';
|
||||
|
||||
const select = (state, props) => {
|
||||
const claim = makeSelectClaimForUri(props.uri)(state);
|
||||
const playingUri = selectPlayingUri(state);
|
||||
const playingUrl = playingUri && playingUri.uri;
|
||||
const claim = makeSelectClaimForUri(playingUrl)(state);
|
||||
const url = claim && claim.permanent_url;
|
||||
|
||||
return {
|
||||
|
|
|
@ -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,13 +65,14 @@ export function CommentCreate(props: Props) {
|
|||
isReply,
|
||||
parentId,
|
||||
activeChannelClaim,
|
||||
bottom,
|
||||
livestream,
|
||||
embed,
|
||||
claimIsMine,
|
||||
sendTip,
|
||||
doToast,
|
||||
} = props;
|
||||
const buttonref: ElementRef<any> = React.useRef();
|
||||
|
||||
const buttonRef: ElementRef<any> = React.useRef();
|
||||
const {
|
||||
push,
|
||||
location: { pathname },
|
||||
|
@ -85,11 +88,8 @@ export function CommentCreate(props: Props) {
|
|||
const [advancedEditor, setAdvancedEditor] = usePersistedState('comment-editor-mode', false);
|
||||
const hasChannels = channels && channels.length;
|
||||
const charCount = commentValue.length;
|
||||
|
||||
const [activeTab, setActiveTab] = React.useState('');
|
||||
|
||||
const [tipError, setTipError] = React.useState();
|
||||
|
||||
const disabled = isSubmitting || !activeChannelClaim || !commentValue.length;
|
||||
const [shouldDisableReviewButton, setShouldDisableReviewButton] = React.useState();
|
||||
|
||||
|
@ -106,9 +106,9 @@ 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();
|
||||
buttonRef.current.click();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,8 +147,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) {
|
||||
|
@ -184,8 +182,7 @@ export function CommentCreate(props: Props) {
|
|||
}
|
||||
|
||||
const sourceClaimId = claim.claim_id;
|
||||
|
||||
var roundedAmount = Math.round(tipAmount * 100) / 100;
|
||||
const roundedAmount = Math.round(tipAmount * 100) / 100;
|
||||
|
||||
Lbryio.call(
|
||||
'customer',
|
||||
|
@ -204,8 +201,6 @@ export function CommentCreate(props: Props) {
|
|||
'post'
|
||||
)
|
||||
.then((customerTipResponse) => {
|
||||
console.log(customerTipResponse);
|
||||
|
||||
const paymentIntendId = customerTipResponse.payment_intent_id;
|
||||
|
||||
handleCreateComment(null, paymentIntendId, stripeEnvironment);
|
||||
|
@ -225,14 +220,14 @@ export function CommentCreate(props: Props) {
|
|||
|
||||
// handleCreateComment(null);
|
||||
})
|
||||
.catch(function (error) {
|
||||
var displayError = 'Sorry, there was an error in processing your payment!';
|
||||
|
||||
if (error.message !== 'payment intent failed to confirm') {
|
||||
displayError = error.message;
|
||||
}
|
||||
|
||||
doToast({ message: displayError, isError: true });
|
||||
.catch((error) => {
|
||||
doToast({
|
||||
message:
|
||||
error.message !== 'payment intent failed to confirm'
|
||||
? error.message
|
||||
: 'Sorry, there was an error in processing your payment!',
|
||||
isError: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +256,7 @@ export function CommentCreate(props: Props) {
|
|||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
.catch(() => {
|
||||
setIsSubmitting(false);
|
||||
setCommentFailure(true);
|
||||
});
|
||||
|
@ -280,6 +275,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);
|
||||
|
@ -306,7 +306,7 @@ export function CommentCreate(props: Props) {
|
|||
<div className="comment__create">
|
||||
<div className="comment__sc-preview">
|
||||
<CreditAmount
|
||||
className="comment__scpreview-amount"
|
||||
className="comment__sc-preview-amount"
|
||||
isFiat={activeTab === TAB_FIAT}
|
||||
amount={tipAmount}
|
||||
size={activeTab === TAB_LBC ? 18 : 2}
|
||||
|
@ -344,6 +344,7 @@ export function CommentCreate(props: Props) {
|
|||
className={classnames('comment__create', {
|
||||
'comment__create--reply': isReply,
|
||||
'comment__create--nested-reply': isNested,
|
||||
'comment__create--bottom': bottom,
|
||||
})}
|
||||
>
|
||||
<FormField
|
||||
|
@ -398,7 +399,7 @@ export function CommentCreate(props: Props) {
|
|||
) : (
|
||||
<>
|
||||
<Button
|
||||
ref={buttonref}
|
||||
ref={buttonRef}
|
||||
button="primary"
|
||||
disabled={disabled}
|
||||
type="submit"
|
||||
|
|
|
@ -44,7 +44,15 @@ const buildIcon = (iconStrokes: React$Node, customSvgValues = {}) =>
|
|||
export const icons = {
|
||||
// The LBRY icon is different from the base icon set so don't use buildIcon()
|
||||
[ICONS.LBRY]: (props: IconProps) => (
|
||||
<svg stroke="currentColor" fill="currentColor" x="0px" y="0px" viewBox="0 0 322 254" className="icon lbry-icon">
|
||||
<svg
|
||||
{...props}
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 322 254"
|
||||
className="icon lbry-icon"
|
||||
>
|
||||
<path d="M296,85.9V100l-138.8,85.3L52.6,134l0.2-7.9l104,51.2L289,96.1v-5.8L164.2,30.1L25,116.2v38.5l131.8,65.2 l137.6-84.4l3.9,6l-141.1,86.4L18.1,159.1v-46.8l145.8-90.2C163.9,22.1,296,85.9,296,85.9z" />
|
||||
<path d="M294.3,150.9l2-12.6l-12.2-2.1l0.8-4.9l17.1,2.9l-2.8,17.5L294.3,150.9L294.3,150.9z" />
|
||||
</svg>
|
||||
|
|
|
@ -73,6 +73,7 @@ class IconComponent extends React.PureComponent<Props> {
|
|||
size={size || (sectionIcon ? 20 : 16)}
|
||||
className={classnames(`icon icon--${icon}`, className, { 'color-override': iconColor })}
|
||||
color={color}
|
||||
aria-hidden
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -11,6 +11,7 @@ type Props = {
|
|||
claimIsMine: boolean,
|
||||
downloading: boolean,
|
||||
loading: boolean,
|
||||
focusable: boolean,
|
||||
fileInfo: ?FileListItem,
|
||||
openModal: (id: string, { path: string }) => void,
|
||||
pause: () => void,
|
||||
|
@ -35,6 +36,7 @@ function FileDownloadLink(props: Props) {
|
|||
uri,
|
||||
claim,
|
||||
buttonType,
|
||||
focusable = true,
|
||||
showLabel = false,
|
||||
hideOpenButton = false,
|
||||
hideDownloadStatus = false,
|
||||
|
@ -91,6 +93,8 @@ function FileDownloadLink(props: Props) {
|
|||
pause();
|
||||
openModal(MODALS.CONFIRM_EXTERNAL_RESOURCE, { path: fileInfo.download_path, isMine: claimIsMine });
|
||||
}}
|
||||
aria-hidden={!focusable}
|
||||
tabIndex={focusable ? 0 : -1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -105,6 +109,8 @@ function FileDownloadLink(props: Props) {
|
|||
icon={ICONS.DOWNLOAD}
|
||||
label={showLabel ? label : null}
|
||||
onClick={handleDownload}
|
||||
aria-hidden={!focusable}
|
||||
tabIndex={focusable ? 0 : -1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ type Props = {
|
|||
index: number,
|
||||
length: number,
|
||||
location: { pathname: string },
|
||||
push: string => void,
|
||||
push: (string) => void,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -43,7 +43,7 @@ function FileDrop(props: Props) {
|
|||
const navigationTimer = React.useRef(null);
|
||||
|
||||
// Gets respective icon given a mimetype
|
||||
const getFileIcon = type => {
|
||||
const getFileIcon = (type) => {
|
||||
// Not all files have a type
|
||||
if (!type) return ICONS.FILE;
|
||||
// Detect common types
|
||||
|
@ -77,10 +77,13 @@ function FileDrop(props: Props) {
|
|||
}, [navigateToPublish]);
|
||||
|
||||
// Handle file selection
|
||||
const handleFileSelected = React.useCallback((selectedFile) => {
|
||||
updatePublishForm({ filePath: selectedFile });
|
||||
hideDropArea();
|
||||
}, [updatePublishForm, hideDropArea]);
|
||||
const handleFileSelected = React.useCallback(
|
||||
(selectedFile) => {
|
||||
updatePublishForm({ filePath: selectedFile });
|
||||
hideDropArea();
|
||||
},
|
||||
[updatePublishForm, hideDropArea]
|
||||
);
|
||||
|
||||
// Clear timers when unmounted
|
||||
React.useEffect(() => {
|
||||
|
@ -114,12 +117,12 @@ function FileDrop(props: Props) {
|
|||
React.useEffect(() => {
|
||||
if (dropData && !files.length && (!modal || modal.id !== MODALS.FILE_SELECTION)) {
|
||||
getTree(dropData)
|
||||
.then(entries => {
|
||||
.then((entries) => {
|
||||
if (entries && entries.length) {
|
||||
setFiles(entries);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
setError(error || true);
|
||||
});
|
||||
}
|
||||
|
@ -146,7 +149,7 @@ function FileDrop(props: Props) {
|
|||
const show = files.length === 1 || (!target && drag && (!modal || modal.id !== MODALS.FILE_SELECTION));
|
||||
|
||||
return (
|
||||
<div className={classnames('file-drop', show && 'file-drop--show')}>
|
||||
<div aria-hidden={!show} className={classnames('file-drop', show && 'file-drop--show')}>
|
||||
<div className={classnames('card', 'file-drop__area')}>
|
||||
<Icon size={64} icon={icon} className={'main-icon'} />
|
||||
<p>{target ? target.name : __(`Drop here to publish!`)} </p>
|
||||
|
|
|
@ -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)}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -7,15 +7,16 @@ import useLazyLoading from 'effects/use-lazy-loading';
|
|||
type Props = {
|
||||
thumb: string,
|
||||
children?: Node,
|
||||
className?: string,
|
||||
};
|
||||
|
||||
const Thumb = (props: Props) => {
|
||||
const { thumb, children } = props;
|
||||
const { thumb, children, className } = props;
|
||||
const thumbnailRef = React.useRef(null);
|
||||
useLazyLoading(thumbnailRef);
|
||||
|
||||
return (
|
||||
<div ref={thumbnailRef} data-background-image={thumb} className={classnames('media__thumb')}>
|
||||
<div ref={thumbnailRef} data-background-image={thumb} className={classnames('media__thumb', className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -53,7 +53,11 @@ function FileThumbnail(props: Props) {
|
|||
const thumbnailUrl = url ? url.replace(/'/g, "\\'") : '';
|
||||
|
||||
if (hasResolvedClaim || thumbnailUrl) {
|
||||
return <Thumb thumb={thumbnailUrl}>{children}</Thumb>;
|
||||
return (
|
||||
<Thumb thumb={thumbnailUrl} className={className}>
|
||||
{children}
|
||||
</Thumb>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<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>
|
||||
|
|
|
@ -8,13 +8,14 @@ import { COLLECTIONS_CONSTS } from 'lbry-redux';
|
|||
type Props = {
|
||||
uri: string,
|
||||
claim: StreamClaim,
|
||||
focusable: boolean,
|
||||
hasClaimInWatchLater: boolean,
|
||||
doToast: ({ message: string }) => void,
|
||||
doCollectionEdit: (string, any) => void,
|
||||
};
|
||||
|
||||
function FileWatchLaterLink(props: Props) {
|
||||
const { claim, hasClaimInWatchLater, doToast, doCollectionEdit } = props;
|
||||
const { claim, hasClaimInWatchLater, doToast, doCollectionEdit, focusable = true } = props;
|
||||
const buttonRef = useRef();
|
||||
let isHovering = useHover(buttonRef);
|
||||
|
||||
|
@ -51,6 +52,8 @@ function FileWatchLaterLink(props: Props) {
|
|||
(isHovering ? ICONS.COMPLETED : ICONS.TIME)
|
||||
}
|
||||
onClick={(e) => handleWatchLater(e)}
|
||||
aria-hidden={!focusable}
|
||||
tabIndex={focusable ? 0 : -1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
@ -15,6 +15,8 @@ import { useIsMobile } from 'effects/use-screensize';
|
|||
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';
|
||||
|
@ -234,6 +236,7 @@ const Header = (props: Props) => {
|
|||
) : (
|
||||
<>
|
||||
<div className="header__navigation">
|
||||
<SkipNavigationButton />
|
||||
{!authHeader && (
|
||||
<span style={{ position: 'relative' }}>
|
||||
<Button
|
||||
|
@ -244,6 +247,7 @@ const Header = (props: Props) => {
|
|||
}
|
||||
className="header__navigation-item menu__title header__navigation-item--icon"
|
||||
icon={ICONS.MENU}
|
||||
aria-expanded={sidebarOpen}
|
||||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||||
>
|
||||
{isAbsoluteSideNavHidden && isMobile && notificationsEnabled && <NotificationBubble />}
|
||||
|
@ -251,14 +255,8 @@ const Header = (props: Props) => {
|
|||
</span>
|
||||
)}
|
||||
<Button
|
||||
aria-label={__('Home')}
|
||||
className="header__navigation-item header__navigation-item--lbry"
|
||||
// @if TARGET='app'
|
||||
label={'LBRY'}
|
||||
// @endif
|
||||
// @if TARGET='web'
|
||||
label={LOGO_TITLE} // eslint-disable-line
|
||||
// @endif
|
||||
icon={ICONS.LBRY}
|
||||
onClick={() => {
|
||||
if (history.location.pathname === '/') window.location.reload();
|
||||
}}
|
||||
|
@ -268,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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -193,7 +193,7 @@ export default function Notification(props: Props) {
|
|||
|
||||
<div className="notification__menu">
|
||||
<Menu>
|
||||
<MenuButton onClick={(e) => e.stopPropagation()}>
|
||||
<MenuButton className={'menu-button notification__menu-button'} onClick={(e) => e.stopPropagation()}>
|
||||
<Icon size={18} icon={ICONS.MORE_VERTICAL} />
|
||||
</MenuButton>
|
||||
<MenuList className="menu__list">
|
||||
|
|
|
@ -109,6 +109,7 @@ function Page(props: Props) {
|
|||
/>
|
||||
)}
|
||||
<main
|
||||
id={'main-content'}
|
||||
className={classnames(MAIN_CLASS, className, {
|
||||
'main--full-width': fullWidthPage,
|
||||
'main--auth-page': authPage,
|
||||
|
|
|
@ -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={() => {
|
||||
|
|
|
@ -13,11 +13,12 @@ type Props = {
|
|||
label: ?string,
|
||||
reward: Reward,
|
||||
button: ?boolean,
|
||||
disabled: boolean,
|
||||
claimReward: (Reward) => void,
|
||||
};
|
||||
|
||||
const RewardLink = (props: Props) => {
|
||||
const { reward, claimReward, label, isPending, button } = props;
|
||||
const { reward, claimReward, label, isPending, button, disabled = false } = props;
|
||||
let displayLabel = label;
|
||||
if (isPending) {
|
||||
displayLabel = __('Claiming...');
|
||||
|
@ -34,7 +35,7 @@ const RewardLink = (props: Props) => {
|
|||
return !reward ? null : (
|
||||
<Button
|
||||
button={button ? 'primary' : 'link'}
|
||||
disabled={isPending}
|
||||
disabled={disabled || isPending}
|
||||
label={<LbcMessage>{displayLabel}</LbcMessage>}
|
||||
aria-label={displayLabel}
|
||||
onClick={() => {
|
||||
|
|
|
@ -23,10 +23,11 @@ type Props = {
|
|||
claim_code: string,
|
||||
},
|
||||
user: User,
|
||||
disabled: boolean,
|
||||
};
|
||||
|
||||
const RewardTile = (props: Props) => {
|
||||
const { reward, openRewardCodeModal, openSetReferrerModal, user } = props;
|
||||
const { reward, openRewardCodeModal, openSetReferrerModal, user, disabled = false } = props;
|
||||
const referrerSet = user && user.invited_by_id;
|
||||
const claimed = !!reward.transaction_id;
|
||||
const customActionsRewards = [rewards.TYPE_REFERRAL, rewards.TYPE_REFEREE];
|
||||
|
@ -38,18 +39,25 @@ const RewardTile = (props: Props) => {
|
|||
actions={
|
||||
<div className="section__actions">
|
||||
{reward.reward_type === rewards.TYPE_GENERATED_CODE && (
|
||||
<Button button="primary" onClick={openRewardCodeModal} label={__('Enter Code')} />
|
||||
<Button button="primary" onClick={openRewardCodeModal} label={__('Enter Code')} disabled={disabled} />
|
||||
)}
|
||||
{reward.reward_type === rewards.TYPE_REFERRAL && (
|
||||
<Button button="primary" navigate="/$/invite" label={__('Go To Invites')} />
|
||||
<Button
|
||||
button="primary"
|
||||
navigate="/$/invite"
|
||||
label={__('Go To Invites')}
|
||||
aria-hidden={disabled}
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
/>
|
||||
)}
|
||||
{reward.reward_type === rewards.TYPE_REFEREE && (
|
||||
<>
|
||||
{referrerSet && <RewardLink button reward_type={reward.reward_type} />}
|
||||
{referrerSet && <RewardLink button reward_type={reward.reward_type} disabled={disabled} />}
|
||||
<Button
|
||||
button={referrerSet ? 'link' : 'primary'}
|
||||
onClick={openSetReferrerModal}
|
||||
label={referrerSet ? __('Change Inviter') : __('Set Inviter')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
@ -59,7 +67,7 @@ const RewardTile = (props: Props) => {
|
|||
<Icon icon={ICONS.COMPLETED} /> {__('Reward claimed.')}
|
||||
</span>
|
||||
) : (
|
||||
<RewardLink button claim_code={reward.claim_code} />
|
||||
<RewardLink button claim_code={reward.claim_code} disabled={disabled} />
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -331,6 +331,7 @@ function SideNavigation(props: Props) {
|
|||
>
|
||||
{!isOnFilePage && (
|
||||
<nav
|
||||
aria-label={'Sidebar'}
|
||||
className={classnames('navigation', {
|
||||
'navigation--micro': microNavigation,
|
||||
// @if TARGET='app'
|
||||
|
|
22
ui/component/skipNavigationButton/index.jsx
Normal file
22
ui/component/skipNavigationButton/index.jsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
// Allow screen reader users ( or keyboard navigation )
|
||||
// to jump to main content
|
||||
export default function SkipNavigationButton() {
|
||||
const skipNavigation = (e) => {
|
||||
// Match any focusable element
|
||||
const focusableElementQuery = `
|
||||
#main-content [tabindex]:not([tabindex="-1"]):not(:disabled),
|
||||
#main-content a:not([aria-hidden]):not([tabindex="-1"]):not(:disabled),
|
||||
#main-content button:not([aria-hidden]):not([tabindex="-1"]):not(:disabled)
|
||||
`;
|
||||
// Find first focusable element
|
||||
const element = document.querySelector(focusableElementQuery);
|
||||
// Trigger focus to skip navigation
|
||||
if (element && element.focus) {
|
||||
element.focus();
|
||||
}
|
||||
};
|
||||
return <Button className={'skip-button'} onClick={skipNavigation} label={__('Skip Navigation')} button={'link'} />;
|
||||
}
|
|
@ -19,6 +19,7 @@ type Props = {
|
|||
inline: boolean,
|
||||
external?: boolean,
|
||||
className?: string,
|
||||
focusable: boolean,
|
||||
};
|
||||
|
||||
class UriIndicator extends React.PureComponent<Props> {
|
||||
|
@ -45,8 +46,9 @@ class UriIndicator extends React.PureComponent<Props> {
|
|||
claim,
|
||||
children,
|
||||
inline,
|
||||
hideAnonymous = false,
|
||||
focusable = true,
|
||||
external = false,
|
||||
hideAnonymous = false,
|
||||
className,
|
||||
} = this.props;
|
||||
|
||||
|
@ -86,7 +88,13 @@ class UriIndicator extends React.PureComponent<Props> {
|
|||
|
||||
if (children) {
|
||||
return (
|
||||
<Button className={className} target={external ? '_blank' : undefined} navigate={channelLink}>
|
||||
<Button
|
||||
aria-hidden={!focusable}
|
||||
tabIndex={focusable ? 0 : -1}
|
||||
className={className}
|
||||
target={external ? '_blank' : undefined}
|
||||
navigate={channelLink}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
|
@ -96,6 +104,8 @@ class UriIndicator extends React.PureComponent<Props> {
|
|||
className={classnames(className, 'button--uri-indicator')}
|
||||
navigate={channelLink}
|
||||
target={external ? '_blank' : undefined}
|
||||
aria-hidden={!focusable}
|
||||
tabIndex={focusable ? 0 : -1}
|
||||
>
|
||||
{inner}
|
||||
</Button>
|
||||
|
|
|
@ -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')} />
|
||||
<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}
|
||||
hideFilters={SIMPLE_SITE}
|
||||
meta={
|
||||
<Button
|
||||
button={subscribedChannels.length < 1 ? 'alt' : 'primary'}
|
||||
onClick={onContinue}
|
||||
label={subscribedChannels.length < 1 ? __('Skip') : __('Continue')}
|
||||
/>
|
||||
}
|
||||
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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
|
||||
import formatMediaDuration from 'util/formatMediaDuration';
|
||||
type Props = {
|
||||
claim: ?StreamClaim,
|
||||
className?: string,
|
||||
|
@ -9,19 +9,11 @@ type Props = {
|
|||
function VideoDuration(props: Props) {
|
||||
const { claim, className } = props;
|
||||
|
||||
const video = claim && claim.value && (claim.value.video || claim.value.audio);
|
||||
const media = claim && claim.value && (claim.value.video || claim.value.audio);
|
||||
let duration;
|
||||
if (video && video.duration) {
|
||||
if (media && media.duration) {
|
||||
// $FlowFixMe
|
||||
let date = new Date(null);
|
||||
date.setSeconds(video.duration);
|
||||
let timeString = date.toISOString().substr(11, 8);
|
||||
|
||||
if (timeString.startsWith('00:')) {
|
||||
timeString = timeString.substr(3);
|
||||
}
|
||||
|
||||
duration = timeString;
|
||||
duration = formatMediaDuration(media.duration);
|
||||
}
|
||||
|
||||
return duration ? <span className={className}>{duration}</span> : null;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
13
ui/index.jsx
13
ui/index.jsx
|
@ -35,7 +35,7 @@ import {
|
|||
doAuthTokenRefresh,
|
||||
} from 'util/saved-passwords';
|
||||
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
|
||||
import { LBRY_WEB_API, DEFAULT_LANGUAGE, LBRY_API_URL } from 'config';
|
||||
import { LBRY_WEB_API, DEFAULT_LANGUAGE, LBRY_API_URL, LBRY_WEB_PUBLISH_API } from 'config';
|
||||
|
||||
// Import 3rd-party styles before ours for the current way we are code-splitting.
|
||||
import 'scss/third-party.scss';
|
||||
|
@ -43,8 +43,12 @@ import 'scss/third-party.scss';
|
|||
// Import our app styles
|
||||
// If a style is not necessary for the initial page load, it should be removed from `all.scss`
|
||||
// and loaded dynamically in the component that consumes it
|
||||
// @if TARGET='app'
|
||||
import 'scss/all.scss';
|
||||
|
||||
// @endif
|
||||
// @if TARGET='web'
|
||||
import 'web/theme';
|
||||
// @endif
|
||||
// @if TARGET='web'
|
||||
// These overrides can't live in web/ because they need to use the same instance of `Lbry`
|
||||
import apiPublishCallViaWeb from 'web/setup/publish';
|
||||
|
@ -65,12 +69,11 @@ if (process.env.SDK_API_URL) {
|
|||
}
|
||||
|
||||
let sdkAPIHost = process.env.SDK_API_HOST || process.env.SDK_API_URL;
|
||||
// @if TARGET='web'
|
||||
sdkAPIHost = LBRY_WEB_API;
|
||||
// @endif
|
||||
|
||||
export const SDK_API_PATH = `${sdkAPIHost}/api/v1`;
|
||||
const proxyURL = `${SDK_API_PATH}/proxy`;
|
||||
const publishURL = LBRY_WEB_PUBLISH_API; // || `${SDK_API_PATH}/proxy`;
|
||||
|
||||
Lbry.setDaemonConnectionString(proxyURL);
|
||||
|
||||
|
@ -80,7 +83,7 @@ Lbry.setOverride(
|
|||
new Promise((resolve, reject) => {
|
||||
apiPublishCallViaWeb(
|
||||
apiCall,
|
||||
proxyURL,
|
||||
publishURL,
|
||||
Lbry.getApiRequestHeaders() && Object.keys(Lbry.getApiRequestHeaders()).includes(X_LBRY_AUTH_TOKEN)
|
||||
? Lbry.getApiRequestHeaders()[X_LBRY_AUTH_TOKEN]
|
||||
: '',
|
||||
|
|
|
@ -14,8 +14,8 @@ type ModalProps = {
|
|||
abortButtonLabel?: string,
|
||||
confirmButtonDisabled?: boolean,
|
||||
abortButtonDisabled?: boolean,
|
||||
onConfirmed?: any => any,
|
||||
onAborted?: any => any,
|
||||
onConfirmed?: (any) => any,
|
||||
onAborted?: (any) => any,
|
||||
className?: string,
|
||||
children?: React.Node,
|
||||
extraContent?: React.Node,
|
||||
|
@ -52,7 +52,13 @@ export function Modal(props: ModalProps) {
|
|||
>
|
||||
{title && <h1 className="card__title card__title--deprecated">{title}</h1>}
|
||||
{type === 'card' && (
|
||||
<Button iconSize={isMobile ? 24 : undefined} button="close" icon={ICONS.REMOVE} onClick={onAborted} />
|
||||
<Button
|
||||
iconSize={isMobile ? 24 : undefined}
|
||||
button="close"
|
||||
aria-label={__('Close')}
|
||||
icon={ICONS.REMOVE}
|
||||
onClick={onAborted}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
{type === 'custom' || type === 'card' ? null : ( // custom modals define their own buttons
|
||||
|
|
|
@ -24,15 +24,20 @@ import ClaimMenuList from 'component/claimMenuList';
|
|||
import OptimizedImage from 'component/optimizedImage';
|
||||
import Yrbl from 'component/yrbl';
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
import TruncatedText from 'component/common/truncated-text';
|
||||
// $FlowFixMe cannot resolve ...
|
||||
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,
|
||||
|
@ -83,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;
|
||||
|
@ -143,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:
|
||||
|
@ -165,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]);
|
||||
|
@ -225,7 +230,9 @@ function ChannelPage(props: Props) {
|
|||
<div className="channel__primary-info">
|
||||
<ChannelThumbnail className="channel__thumbnail--channel-page" uri={uri} allowGifs hideStakedIndicator />
|
||||
<h1 className="channel__title">
|
||||
{title || '@' + channelName}
|
||||
<TruncatedText lines={2} showTooltip>
|
||||
{title || '@' + channelName}
|
||||
</TruncatedText>
|
||||
<ChannelStakedIndicator uri={uri} large />
|
||||
</h1>
|
||||
<div className="channel__meta">
|
||||
|
@ -241,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}
|
||||
|
@ -284,27 +291,31 @@ function ChannelPage(props: Props) {
|
|||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<ChannelContent
|
||||
uri={uri}
|
||||
channelIsBlackListed={channelIsBlackListed}
|
||||
viewHiddenChannels
|
||||
empty={<section className="main--empty">{__('No Content Found')}</section>}
|
||||
/>
|
||||
{currentView === PAGE.CONTENT && (
|
||||
<ChannelContent
|
||||
uri={uri}
|
||||
channelIsBlackListed={channelIsBlackListed}
|
||||
viewHiddenChannels
|
||||
empty={<section className="main--empty">{__('No Content Found')}</section>}
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<ChannelContent
|
||||
claimType={'collection'}
|
||||
uri={uri}
|
||||
channelIsBlackListed={channelIsBlackListed}
|
||||
viewHiddenChannels
|
||||
empty={collectionEmpty}
|
||||
/>
|
||||
{currentView === PAGE.LISTS && (
|
||||
<ChannelContent
|
||||
claimType={'collection'}
|
||||
uri={uri}
|
||||
channelIsBlackListed={channelIsBlackListed}
|
||||
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
|
||||
|
|
|
@ -62,7 +62,6 @@ function HomePage(props: Props) {
|
|||
showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS}
|
||||
hasSource
|
||||
pinUrls={pinUrls}
|
||||
pin={route === `/$/${PAGES.GENERAL}`} // use pinUrls here
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -145,12 +144,12 @@ 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);
|
||||
})}
|
||||
{/* @if TARGET='web' */}
|
||||
<Pixel type={'retargeting'} />
|
||||
<Pixel type={'retargeting'} />
|
||||
{/* @endif */}
|
||||
</Page>
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -102,6 +102,8 @@ class RewardsPage extends PureComponent<Props> {
|
|||
}
|
||||
|
||||
renderCustomRewardCode() {
|
||||
const { user } = this.props;
|
||||
const isNotEligible = !user || !user.primary_email || !user.has_verified_email || !user.is_reward_approved;
|
||||
return (
|
||||
<RewardTile
|
||||
key={REWARD_TYPES.TYPE_GENERATED_CODE}
|
||||
|
@ -110,6 +112,7 @@ class RewardsPage extends PureComponent<Props> {
|
|||
reward_title: __('Custom Code'),
|
||||
reward_description: __('Are you a supermodel or rockstar that received a custom reward code? Claim it here.'),
|
||||
}}
|
||||
disabled={isNotEligible}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -155,12 +158,13 @@ class RewardsPage extends PureComponent<Props> {
|
|||
|
||||
return (
|
||||
<div
|
||||
aria-hidden={isNotEligible}
|
||||
className={classnames('card__list', {
|
||||
'card--disabled': isNotEligible,
|
||||
})}
|
||||
>
|
||||
{rewards.map((reward) => (
|
||||
<RewardTile key={reward.claim_code} reward={reward} />
|
||||
<RewardTile disabled={isNotEligible} key={reward.claim_code} reward={reward} />
|
||||
))}
|
||||
{this.renderCustomRewardCode()}
|
||||
</div>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -236,6 +236,7 @@
|
|||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
background-color: var(--color-button-alt-bg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
$cover-z-index: 0;
|
||||
$metadata-z-index: 1;
|
||||
$actions-z-index: 2;
|
||||
|
||||
.channel-cover {
|
||||
position: relative;
|
||||
|
@ -200,7 +201,7 @@ $metadata-z-index: 1;
|
|||
top: 0;
|
||||
right: var(--spacing-m);
|
||||
margin-top: var(--spacing-m);
|
||||
z-index: $metadata-z-index;
|
||||
z-index: $actions-z-index;
|
||||
flex-wrap: wrap;
|
||||
font-size: var(--font-base);
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
|
||||
&:hover {
|
||||
.claim__menu-button {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -269,7 +269,7 @@
|
|||
.claim-preview__title {
|
||||
font-weight: var(--font-weight-bold);
|
||||
font-size: var(--font-body);
|
||||
margin-right: auto;
|
||||
margin-right: var(--spacing-l);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
|
@ -475,7 +475,7 @@
|
|||
cursor: pointer;
|
||||
|
||||
.claim__menu-button {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
.collection-preview__overlay-thumbs {
|
||||
opacity: 1;
|
||||
|
@ -531,7 +531,7 @@
|
|||
.claim-tile__title {
|
||||
position: relative;
|
||||
padding: var(--spacing-s);
|
||||
padding-right: var(--spacing-l);
|
||||
padding-right: var(--spacing-xl);
|
||||
padding-bottom: 0;
|
||||
margin-bottom: var(--spacing-s);
|
||||
|
||||
|
@ -540,14 +540,8 @@
|
|||
font-size: var(--font-small);
|
||||
min-height: 2rem;
|
||||
|
||||
.claim__menu-button {
|
||||
right: 0.2rem;
|
||||
top: var(--spacing-s);
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
min-height: 2.5rem;
|
||||
padding-right: var(--spacing-m);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -745,29 +739,38 @@
|
|||
}
|
||||
}
|
||||
|
||||
.claim__menu-button {
|
||||
position: absolute;
|
||||
top: var(--spacing-xs);
|
||||
right: var(--spacing-xs);
|
||||
.claim-tile__header {
|
||||
position: relative;
|
||||
|
||||
.icon {
|
||||
stroke: var(--color-text);
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
&:not([aria-expanded='true']) {
|
||||
display: none;
|
||||
}
|
||||
.claim__menu-button {
|
||||
right: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.claim__menu-button--inline {
|
||||
position: relative;
|
||||
display: block;
|
||||
right: auto;
|
||||
top: auto;
|
||||
@extend .button--alt;
|
||||
padding: 0 var(--spacing-xxs);
|
||||
.menu__button {
|
||||
&.claim__menu-button {
|
||||
position: absolute;
|
||||
top: var(--spacing-xs);
|
||||
right: var(--spacing-xs);
|
||||
}
|
||||
|
||||
&.claim__menu-button--inline {
|
||||
position: relative;
|
||||
@extend .button--alt;
|
||||
width: var(--height-button);
|
||||
padding: 0;
|
||||
border-radius: var(--border-radius);
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
.claim-preview--tile:not(:hover),
|
||||
.claim-preview__wrapper:not(:hover) {
|
||||
.claim__menu-button:not(:focus):not([aria-expanded='true']) {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.claim-preview__overlay-properties {
|
||||
|
|
|
@ -127,7 +127,7 @@ $thumbnailWidthSmall: 1rem;
|
|||
margin-top: var(--spacing-xxs);
|
||||
}
|
||||
|
||||
.comment__scpreview-amount {
|
||||
.comment__sc-preview-amount {
|
||||
margin-right: var(--spacing-m);
|
||||
font-size: var(--font-large);
|
||||
}
|
||||
|
@ -263,14 +263,6 @@ $thumbnailWidthSmall: 1rem;
|
|||
.comment__menu {
|
||||
align-self: flex-end;
|
||||
line-height: 1;
|
||||
|
||||
button {
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
&:focus {
|
||||
@include linkFocus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comment__char-count {
|
||||
|
@ -295,14 +287,6 @@ $thumbnailWidthSmall: 1rem;
|
|||
}
|
||||
}
|
||||
|
||||
.comment__menu-icon--hovering {
|
||||
stroke: var(--color-comment-menu-hovering);
|
||||
}
|
||||
|
||||
.comment__menu-icon {
|
||||
stroke: var(--color-comment-menu);
|
||||
}
|
||||
|
||||
.comment__menu-list {
|
||||
box-shadow: var(--card-box-shadow);
|
||||
border-radius: var(--card-radius);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import 'init/mixins';
|
||||
@import '../init/mixins';
|
||||
|
||||
input,
|
||||
textarea,
|
||||
|
|
|
@ -9,6 +9,25 @@
|
|||
-webkit-user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
|
||||
.skip-button {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
margin-right: var(--spacing-l);
|
||||
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
overflow: unset;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
& > * {
|
||||
user-select: none;
|
||||
}
|
||||
|
@ -58,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);
|
||||
|
||||
|
@ -132,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);
|
||||
|
|
|
@ -318,7 +318,7 @@ $discussion-header__height: 3rem;
|
|||
padding-bottom: var(--spacing-xxs);
|
||||
.markdown-preview {
|
||||
p {
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -218,14 +218,6 @@ $contentMaxWidth: 60rem;
|
|||
.icon {
|
||||
stroke: var(--color-text-help);
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
&:focus {
|
||||
@include linkFocus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notification__filter {
|
||||
|
|
|
@ -44,10 +44,25 @@
|
|||
}
|
||||
|
||||
.menu__button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 100%;
|
||||
padding: 0.3rem;
|
||||
|
||||
.icon {
|
||||
stroke: var(--color-menu);
|
||||
}
|
||||
|
||||
.comment__menu-icon--hovering {
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background-color: var(--color-button-alt-bg);
|
||||
.icon {
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--color-card-background-highlighted);
|
||||
stroke: var(--color-menu-hovering);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,10 +124,10 @@
|
|||
--color-menu-background: var(--color-header-background);
|
||||
--color-menu-background--active: var(--color-card-background-highlighted);
|
||||
--color-menu-icon: var(--color-navigation-link);
|
||||
--color-menu: var(--color-gray-3);
|
||||
--color-menu-hovering: var(--color-gray-6);
|
||||
|
||||
// Comments
|
||||
--color-comment-menu: var(--color-gray-3);
|
||||
--color-comment-menu-hovering: var(--color-gray-6);
|
||||
--color-comment-highlighted: #fff2d9;
|
||||
--color-comment-threadline: var(--color-gray-1);
|
||||
--color-comment-threadline-hover: var(--color-gray-4);
|
||||
|
|
|
@ -90,10 +90,10 @@
|
|||
--color-menu-background: var(--color-header-background);
|
||||
--color-menu-background--active: var(--color-gray-7);
|
||||
--color-menu-icon: var(--color-gray-4);
|
||||
--color-menu: var(--color-gray-5);
|
||||
--color-menu-hovering: var(--color-gray-2);
|
||||
|
||||
// Comments
|
||||
--color-comment-menu: var(--color-gray-5);
|
||||
--color-comment-menu-hovering: var(--color-gray-2);
|
||||
--color-comment-threadline: #434b54;
|
||||
--color-comment-threadline-hover: var(--color-gray-4);
|
||||
--color-comment-highlighted: #484734;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
32
ui/util/formatAriaLabel.js
Normal file
32
ui/util/formatAriaLabel.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import DateTime from 'component/dateTime';
|
||||
|
||||
export function formatClaimPreviewTitle(title, channelTitle, date, mediaDuration) {
|
||||
// Aria-label value for claim preview
|
||||
let ariaDate = date ? DateTime.getTimeAgoStr(date) : null;
|
||||
let ariaLabelData = title;
|
||||
|
||||
if (mediaDuration) {
|
||||
if (ariaDate) {
|
||||
ariaLabelData = __('%title% by %channelTitle% %ariaDate%, %mediaDuration%', {
|
||||
title,
|
||||
channelTitle,
|
||||
ariaDate,
|
||||
mediaDuration,
|
||||
});
|
||||
} else {
|
||||
ariaLabelData = __('%title% by %channelTitle%, %mediaDuration%', {
|
||||
title,
|
||||
channelTitle,
|
||||
mediaDuration,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (ariaDate) {
|
||||
ariaLabelData = __('%title% by %channelTitle% %ariaDate%', { title, channelTitle, ariaDate });
|
||||
} else {
|
||||
ariaLabelData = __('%title% by %channelTitle%', { title, channelTitle });
|
||||
}
|
||||
}
|
||||
|
||||
return ariaLabelData;
|
||||
}
|
24
ui/util/formatMediaDuration.js
Normal file
24
ui/util/formatMediaDuration.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import moment from 'moment';
|
||||
|
||||
export default function formatMediaDuration(duration = 0, config) {
|
||||
const options = {
|
||||
screenReader: false,
|
||||
...config,
|
||||
};
|
||||
|
||||
// Optimize for screen readers
|
||||
if (options.screenReader) {
|
||||
return moment.utc(moment.duration(duration, 'seconds').asMilliseconds()).format('HH:mm:ss');
|
||||
}
|
||||
|
||||
// Normal format
|
||||
let date = new Date(null);
|
||||
date.setSeconds(duration);
|
||||
|
||||
let timeString = date.toISOString().substr(11, 8);
|
||||
if (timeString.startsWith('00:')) {
|
||||
timeString = timeString.substr(3);
|
||||
}
|
||||
|
||||
return timeString;
|
||||
}
|
|
@ -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} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
<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>
|
||||
<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>
|
||||
);
|
||||
|
|
70
web/scss/lbrytv.scss
Normal file
70
web/scss/lbrytv.scss
Normal file
|
@ -0,0 +1,70 @@
|
|||
@charset "utf-8";
|
||||
|
||||
@import '../../ui/scss/init/reset';
|
||||
@import '../../ui/scss/init/vars';
|
||||
@import '../../ui/scss/init/mixins';
|
||||
@import '../../ui/scss/init/gui';
|
||||
@import '../../ui/scss/init/base-theme';
|
||||
|
||||
@import 'themes/lbrytv/light.scss';
|
||||
@import 'themes/lbrytv/dark.scss';
|
||||
@import '../../ui/scss/component/ads';
|
||||
@import '../../ui/scss/component/animation';
|
||||
@import '../../ui/scss/component/badge';
|
||||
@import '../../ui/scss/component/block-list';
|
||||
@import '../../ui/scss/component/button';
|
||||
@import '../../ui/scss/component/card';
|
||||
@import '../../ui/scss/component/channel';
|
||||
@import '../../ui/scss/component/claim-list';
|
||||
@import '../../ui/scss/component/collection';
|
||||
@import '../../ui/scss/component/comments';
|
||||
@import '../../ui/scss/component/content';
|
||||
@import '../../ui/scss/component/dat-gui';
|
||||
@import '../../ui/scss/component/embed-player';
|
||||
@import '../../ui/scss/component/expandable';
|
||||
@import '../../ui/scss/component/expanding-details';
|
||||
@import '../../ui/scss/component/file-drop';
|
||||
@import '../../ui/scss/component/file-list';
|
||||
@import '../../ui/scss/component/file-properties';
|
||||
@import '../../ui/scss/component/file-render';
|
||||
@import '../../ui/scss/component/footer';
|
||||
@import '../../ui/scss/component/form-field';
|
||||
@import '../../ui/scss/component/header';
|
||||
@import '../../ui/scss/component/icon';
|
||||
@import '../../ui/scss/component/main';
|
||||
@import '../../ui/scss/component/markdown-editor';
|
||||
@import '../../ui/scss/component/markdown-preview';
|
||||
@import '../../ui/scss/component/media';
|
||||
@import '../../ui/scss/component/menu-button';
|
||||
@import '../../ui/scss/component/modal';
|
||||
@import '../../ui/scss/component/nag';
|
||||
@import '../../ui/scss/component/navigation';
|
||||
@import '../../ui/scss/component/notification';
|
||||
@import '../../ui/scss/component/nudge';
|
||||
@import '../../ui/scss/component/pagination';
|
||||
@import '../../ui/scss/component/post';
|
||||
@import '../../ui/scss/component/purchase';
|
||||
@import '../../ui/scss/component/placeholder';
|
||||
@import '../../ui/scss/component/progress';
|
||||
@import '../../ui/scss/component/search';
|
||||
@import '../../ui/scss/component/claim-search';
|
||||
@import '../../ui/scss/component/section';
|
||||
@import '../../ui/scss/component/share';
|
||||
@import '../../ui/scss/component/snack-bar';
|
||||
@import '../../ui/scss/component/spinner';
|
||||
@import '../../ui/scss/component/splash';
|
||||
@import '../../ui/scss/component/status-bar';
|
||||
@import '../../ui/scss/component/superchat';
|
||||
@import '../../ui/scss/component/syntax-highlighter';
|
||||
@import '../../ui/scss/component/table';
|
||||
@import '../../ui/scss/component/livestream';
|
||||
@import '../../ui/scss/component/tabs';
|
||||
@import '../../ui/scss/component/tooltip';
|
||||
@import '../../ui/scss/component/txo-list';
|
||||
@import '../../ui/scss/component/videojs';
|
||||
@import '../../ui/scss/component/tags';
|
||||
@import '../../ui/scss/component/wunderbar';
|
||||
@import '../../ui/scss/component/yrbl';
|
||||
@import '../../ui/scss/component/empty';
|
||||
@import '../../ui/scss/component/stripe-card';
|
||||
@import '../../ui/scss/component/wallet-tip-send';
|
70
web/scss/odysee.scss
Normal file
70
web/scss/odysee.scss
Normal file
|
@ -0,0 +1,70 @@
|
|||
@charset "utf-8";
|
||||
|
||||
@import 'themes/odysee/init/reset';
|
||||
@import 'themes/odysee/init/vars';
|
||||
@import 'themes/odysee/init/mixins';
|
||||
@import 'themes/odysee/init/gui';
|
||||
@import 'themes/odysee/init/base-theme';
|
||||
|
||||
@import 'themes/odysee/light.scss';
|
||||
@import 'themes/odysee/dark.scss';
|
||||
@import '../../ui/scss/component/ads';
|
||||
@import '../../ui/scss/component/animation';
|
||||
@import '../../ui/scss/component/badge';
|
||||
@import '../../ui/scss/component/block-list';
|
||||
@import '../../ui/scss/component/button';
|
||||
@import '../../ui/scss/component/card';
|
||||
@import '../../ui/scss/component/channel';
|
||||
@import '../../ui/scss/component/claim-list';
|
||||
@import '../../ui/scss/component/collection';
|
||||
@import '../../ui/scss/component/comments';
|
||||
@import '../../ui/scss/component/content';
|
||||
@import '../../ui/scss/component/dat-gui';
|
||||
@import '../../ui/scss/component/embed-player';
|
||||
@import '../../ui/scss/component/expandable';
|
||||
@import '../../ui/scss/component/expanding-details';
|
||||
@import '../../ui/scss/component/file-drop';
|
||||
@import '../../ui/scss/component/file-list';
|
||||
@import '../../ui/scss/component/file-properties';
|
||||
@import '../../ui/scss/component/file-render';
|
||||
@import '../../ui/scss/component/footer';
|
||||
@import '../../ui/scss/component/form-field';
|
||||
@import '../../ui/scss/component/header';
|
||||
@import '../../ui/scss/component/icon';
|
||||
@import '../../ui/scss/component/main';
|
||||
@import '../../ui/scss/component/markdown-editor';
|
||||
@import '../../ui/scss/component/markdown-preview';
|
||||
@import '../../ui/scss/component/media';
|
||||
@import '../../ui/scss/component/menu-button';
|
||||
@import '../../ui/scss/component/modal';
|
||||
@import '../../ui/scss/component/nag';
|
||||
@import '../../ui/scss/component/navigation';
|
||||
@import '../../ui/scss/component/notification';
|
||||
@import '../../ui/scss/component/nudge';
|
||||
@import '../../ui/scss/component/pagination';
|
||||
@import '../../ui/scss/component/post';
|
||||
@import '../../ui/scss/component/purchase';
|
||||
@import '../../ui/scss/component/placeholder';
|
||||
@import '../../ui/scss/component/progress';
|
||||
@import '../../ui/scss/component/search';
|
||||
@import '../../ui/scss/component/claim-search';
|
||||
@import '../../ui/scss/component/section';
|
||||
@import '../../ui/scss/component/share';
|
||||
@import '../../ui/scss/component/snack-bar';
|
||||
@import '../../ui/scss/component/spinner';
|
||||
@import '../../ui/scss/component/splash';
|
||||
@import '../../ui/scss/component/status-bar';
|
||||
@import '../../ui/scss/component/superchat';
|
||||
@import '../../ui/scss/component/syntax-highlighter';
|
||||
@import '../../ui/scss/component/table';
|
||||
@import '../../ui/scss/component/livestream';
|
||||
@import '../../ui/scss/component/tabs';
|
||||
@import '../../ui/scss/component/tooltip';
|
||||
@import '../../ui/scss/component/txo-list';
|
||||
@import '../../ui/scss/component/videojs';
|
||||
@import '../../ui/scss/component/tags';
|
||||
@import '../../ui/scss/component/wunderbar';
|
||||
@import '../../ui/scss/component/yrbl';
|
||||
@import '../../ui/scss/component/empty';
|
||||
@import '../../ui/scss/component/stripe-card';
|
||||
@import '../../ui/scss/component/wallet-tip-send';
|
159
web/scss/themes/lbrytv/dark.scss
Normal file
159
web/scss/themes/lbrytv/dark.scss
Normal file
|
@ -0,0 +1,159 @@
|
|||
[theme='dark'] {
|
||||
// Color overrides
|
||||
--color-primary: #2bbb90;
|
||||
--color-primary-alt: #3e675d;
|
||||
--color-primary-alt-2: #065f46;
|
||||
--color-primary-alt-3: #34e5b0;
|
||||
--color-secondary: #204166;
|
||||
--color-secondary-alt: #dbeafe;
|
||||
--color-secondary-alt-2: #bfdbfe;
|
||||
--color-secondary-alt-3: #2c5c8c;
|
||||
|
||||
// Structure
|
||||
--color-background: var(--color-gray-9);
|
||||
--color-background-overlay: #21252999;
|
||||
--color-border: #333338;
|
||||
--color-card-background: var(--color-gray-8);
|
||||
--color-card-background-highlighted: var(--color-gray-7);
|
||||
|
||||
// Text
|
||||
--color-text: var(--color-white);
|
||||
--color-text-subtitle: var(--color-gray-4);
|
||||
--color-text-empty: var(--color-text-subtitle);
|
||||
--color-text-help: #bbbbbb;
|
||||
--color-text-warning: #212529;
|
||||
--color-text-warning--background: var(--lbry-yellow-1);
|
||||
--color-text-error: #f87171;
|
||||
--color-error: #61373f;
|
||||
|
||||
// Tags (words)
|
||||
--color-tag-words: var(--color-text);
|
||||
--color-tag-words-bg: var(--color-gray-5);
|
||||
--color-tag-words-hover: var(--color-white);
|
||||
--color-tag-words-bg-hover: var(--color-gray-4);
|
||||
|
||||
// Header
|
||||
--color-header-background: var(--color-gray-8);
|
||||
--color-header-button: var(--color-gray-6);
|
||||
--color-header-button-hover: var(--color-gray-6);
|
||||
--color-header-button-active: var(--color-gray-6);
|
||||
|
||||
// Button
|
||||
--color-button-primary-bg: var(--color-primary-alt);
|
||||
--color-button-primary-bg-hover: var(--color-primary-alt-2);
|
||||
--color-button-primary-text: var(--color-gray-2);
|
||||
--color-button-primary-hover-text: var(--color-primary-alt);
|
||||
--color-button-secondary-bg: var(--color-secondary);
|
||||
--color-button-secondary-border: var(--color-secondary);
|
||||
--color-button-secondary-bg-hover: var(--color-secondary-alt-3);
|
||||
--color-button-secondary-text: var(--color-gray-2);
|
||||
--color-button-alt-bg: var(--color-gray-7);
|
||||
--color-button-alt-bg-hover: var(--color-gray-6);
|
||||
--color-button-alt-text: var(--color-gray-1);
|
||||
--color-button-border: var(--color-gray-5);
|
||||
--color-button-toggle-text: var(--color-gray-1);
|
||||
--color-link: var(--color-primary-alt-3);
|
||||
--color-link-hover: var(--color-text);
|
||||
--color-link-focus-bg: var(--color-gray-7);
|
||||
|
||||
// Input
|
||||
--color-input: var(--color-white);
|
||||
--color-input-label: var(--color-gray-3);
|
||||
--color-input-placeholder: var(--color-gray-1);
|
||||
--color-input-bg: var(--color-header-button);
|
||||
--color-input-bg-copyable: var(--color-gray-6);
|
||||
--color-input-border: var(--color-border);
|
||||
--color-input-border-active: var(--color-secondary);
|
||||
--color-input-toggle: var(--color-primary-alt-3);
|
||||
--color-input-toggle-bg: var(--color-input-bg);
|
||||
--color-input-toggle-bg-hover: var(--color-secondary);
|
||||
--color-input-bg-selected: var(--color-primary-alt);
|
||||
--color-input-prefix-bg: var(--color-gray-5);
|
||||
--color-input-prefix-border: var(--color-gray-4);
|
||||
--select-toggle-background: url("data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg' fill='%23ffffff'%3E%3Cpath d='M17.172, 31.172c1.562, -1.562 4.095, -1.562 5.656, 0l25.172, 25.171l25.172, -25.171c1.562, -1.562 4.095, -1.562 5.656, 0c1.562, 1.562 1.562, 4.095 0, 5.656l-28, 28c-1.562, 1.562 -4.095, 1.562 -5.656, 0l-28, -28c-0.781, -0.781 -1.172, -1.805 -1.172, -2.828c0, -1.023 0.391, -2.047 1.172, -2.828Z'/%3E%3C/svg%3E%0A");
|
||||
|
||||
// Navigation
|
||||
--color-navigation-icon: var(--color-gray-4);
|
||||
--color-navigation-link: var(--color-gray-4);
|
||||
--color-navigation-active: var(--color-gray-7);
|
||||
--color-navigation-active-text: var(--color-gray-3);
|
||||
--color-navigation-hover: var(--color-gray-6);
|
||||
--color-navigation-hover-text: var(--color-gray-3);
|
||||
|
||||
// Tags
|
||||
--color-tag: var(--color-primary-alt-3);
|
||||
--color-tag-bg: var(--color-gray-7);
|
||||
--color-tag-hover: var(--color-white);
|
||||
--color-tag-bg-hover: var(--color-primary-alt);
|
||||
|
||||
// Menu
|
||||
--color-menu-background: var(--color-header-background);
|
||||
--color-menu-background--active: var(--color-gray-7);
|
||||
--color-menu-icon: var(--color-gray-4);
|
||||
|
||||
// Comments
|
||||
--color-comment-menu: var(--color-gray-5);
|
||||
--color-comment-menu-hovering: var(--color-gray-2);
|
||||
--color-comment-threadline: #434b54;
|
||||
--color-comment-threadline-hover: var(--color-gray-4);
|
||||
--color-comment-highlighted: #484734;
|
||||
|
||||
// Snack
|
||||
--color-snack-bg: var(--color-secondary);
|
||||
|
||||
// Superchat
|
||||
--color-superchat-text: var(--color-black);
|
||||
--color-superchat-text__light: var(--color-text);
|
||||
--color-superchat: #fcd34d;
|
||||
--color-superchat__light: #ef4e1647;
|
||||
--color-superchat-2: #fde68a;
|
||||
--color-superchat-3: #fef3c7;
|
||||
--color-superchat-3__light: #58066087;
|
||||
--color-superchat-4: #fffbeb;
|
||||
|
||||
// Other
|
||||
--color-focus: #93c5fd50;
|
||||
--color-nag: var(--color-orange);
|
||||
--color-tab-text: var(--color-white);
|
||||
--color-tabs-background: var(--color-card-background);
|
||||
--color-tab-divider: var(--color-white);
|
||||
--color-modal-background: var(--color-card-background);
|
||||
--color-notice: #58563b;
|
||||
--color-purchased: #ffd580;
|
||||
--color-purchased-alt: var(--color-purchased);
|
||||
--color-purchased-text: var(--color-gray-5);
|
||||
--color-thumbnail-background: var(--color-gray-5);
|
||||
--color-tooltip-bg: #2f3337;
|
||||
--color-help-warning-bg: #d97706;
|
||||
--color-help-warning-text: white;
|
||||
--color-blockquote: var(--color-gray-5);
|
||||
--color-placeholder-background: #4e5862;
|
||||
--color-spinner-light: #5a6570;
|
||||
--color-spinner-dark: #212529;
|
||||
--color-login-graphic-background: var(--color-background);
|
||||
|
||||
// Editor
|
||||
--color-editor-cursor: var(--color-text);
|
||||
--color-editor-quote: #d3d3d3;
|
||||
--color-editor-tag: #efbe5d;
|
||||
--color-editor-attr: #68ccf9;
|
||||
--color-editor-string: #ff8b6b;
|
||||
--color-editor-inline-code-fg: #ce9178;
|
||||
--color-editor-inline-code-fg-preview: #e8b692;
|
||||
--color-editor-inline-code-bg: rgba(20, 68, 102, 0.3);
|
||||
--color-editor-inline-code-bg-preview: #464b50;
|
||||
--color-editor-selected: #264f78;
|
||||
--color-editor-link: var(--color-link);
|
||||
--color-editor-url: var(--color-editor-string);
|
||||
--color-editor-hr: var(--color-editor-tag);
|
||||
--color-editor-hr-preview: #a0a0a0;
|
||||
|
||||
// Ads
|
||||
--color-ads-background: #475057;
|
||||
--color-ads-text: #111;
|
||||
--color-ads-link: var(--color-primary-alt);
|
||||
|
||||
// Scrollbar
|
||||
--color-scrollbar-thumb-bg: rgba(255, 255, 255, 0.2);
|
||||
--color-scrollbar-track-bg: transparent;
|
||||
}
|
2
web/scss/themes/lbrytv/light.scss
Normal file
2
web/scss/themes/lbrytv/light.scss
Normal file
|
@ -0,0 +1,2 @@
|
|||
:root {
|
||||
}
|
755
web/scss/themes/odysee/component/_file-render.scss
Normal file
755
web/scss/themes/odysee/component/_file-render.scss
Normal file
|
@ -0,0 +1,755 @@
|
|||
.file-page {
|
||||
.file-page__video-container + .card,
|
||||
.file-render + .card,
|
||||
.content__cover + .card,
|
||||
.card + .file-render,
|
||||
.card + .file-page__video-container,
|
||||
.card + .content__cover {
|
||||
margin-top: var(--spacing-m);
|
||||
}
|
||||
|
||||
.card + .file-render {
|
||||
margin-top: var(--spacing-m);
|
||||
}
|
||||
|
||||
.file-page__md {
|
||||
.file-viewer--document {
|
||||
margin-top: var(--spacing-l);
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
margin-top: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline;
|
||||
|
||||
.button__content {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.claim-link {
|
||||
.button {
|
||||
display: block;
|
||||
|
||||
.button__content {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media__actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.file-page__secondary-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 var(--spacing-m);
|
||||
}
|
||||
}
|
||||
|
||||
> * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-medium) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.file-render {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
max-height: var(--inline-player-max-height);
|
||||
}
|
||||
|
||||
.file-render--video {
|
||||
background-color: black;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: black;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
animation: fadeInFromBlack 2s ease;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInFromBlack {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.file-render--embed {
|
||||
border-radius: 0;
|
||||
position: fixed;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.file-render--img-container {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.file-render--post-container {
|
||||
min-height: 30vh;
|
||||
}
|
||||
|
||||
.file-render__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.file-viewer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
iframe,
|
||||
webview,
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
max-height: var(--inline-player-max-height);
|
||||
}
|
||||
video {
|
||||
cursor: pointer;
|
||||
}
|
||||
.video-js.vjs-user-inactive.vjs-playing {
|
||||
video {
|
||||
cursor: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-render__viewer--comic {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.comic-viewer {
|
||||
width: 100%;
|
||||
height: calc(100vh - var(--header-height) - var(--spacing-m) * 2);
|
||||
max-height: var(--inline-player-max-height);
|
||||
}
|
||||
}
|
||||
|
||||
.file-viewer--iframe {
|
||||
display: flex; /*this eliminates extra height from whitespace, if someone edits this with a better technique, tell Jeremy*/
|
||||
/*
|
||||
ideally iframes would dynamiclly grow, see <IframeReact> for a start at this
|
||||
for now, since we don't know size, let's make as large as we can without being larger than available area
|
||||
*/
|
||||
iframe {
|
||||
height: calc(100vh - var(--header-height) - var(--spacing-m) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
.file-render__viewer--three {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.three-viewer {
|
||||
height: calc(100vh - var(--header-height) - var(--spacing-m) * 2);
|
||||
max-height: var(--inline-player-max-height);
|
||||
}
|
||||
}
|
||||
|
||||
.file-viewer__overlay {
|
||||
position: absolute;
|
||||
left: auto;
|
||||
right: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
color: var(--color-white);
|
||||
font-size: var(--font-body); /* put back font size from videojs change*/
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--spacing-l);
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
font-size: var(--font-small);
|
||||
}
|
||||
|
||||
.button--uri-indicator,
|
||||
.button--link {
|
||||
color: var(--color-white);
|
||||
}
|
||||
}
|
||||
|
||||
.content__viewer--floating {
|
||||
.file-viewer__overlay-title,
|
||||
.file-viewer__overlay-secondary {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
@media (max-width: $breakpoint-small) {
|
||||
.file-viewer__overlay-title,
|
||||
.file-viewer__overlay-secondary {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.file-viewer__overlay-title {
|
||||
text-align: center;
|
||||
font-size: var(--font-large);
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
.file-viewer__overlay-secondary {
|
||||
color: var(--color-text-subtitle);
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
.file-viewer__overlay-actions {
|
||||
.button + .button {
|
||||
margin-left: var(--spacing-m);
|
||||
}
|
||||
}
|
||||
|
||||
.file-viewer__overlay-logo {
|
||||
height: 3.5rem;
|
||||
width: 12rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
filter: drop-shadow(1px 2px 10px var(--color-gray-3));
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
margin-right: var(--spacing-m);
|
||||
width: 2.5rem;
|
||||
|
||||
.button__label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-viewer__overlay-logo--videoend {
|
||||
height: 3.5rem;
|
||||
width: 12rem;
|
||||
}
|
||||
|
||||
.file-viewer--is-playing:not(:hover) .file-viewer__embedded-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-viewer__embedded-header {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
z-index: 2;
|
||||
height: 4rem;
|
||||
padding-left: var(--spacing-m);
|
||||
padding-right: var(--spacing-s);
|
||||
font-size: var(--font-large);
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-top-right-radius: var(--border-radius);
|
||||
|
||||
.button {
|
||||
color: var(--color-white);
|
||||
z-index: 2;
|
||||
|
||||
.button__label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-white);
|
||||
}
|
||||
}
|
||||
|
||||
.credit-amount,
|
||||
.icon--Key {
|
||||
margin-right: var(--spacing-m);
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
padding: 0 var(--spacing-l);
|
||||
}
|
||||
}
|
||||
|
||||
.file-viewer__embedded-gradient {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
background: linear-gradient(#000000, #00000000 70%);
|
||||
height: 75px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.file-viewer__embedded-title {
|
||||
max-width: 75%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.file-viewer__embedded-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--font-small);
|
||||
margin-left: var(--spacing-m);
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: var(--spacing-s);
|
||||
}
|
||||
}
|
||||
|
||||
.file-render__content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
//
|
||||
// Custom viewers live below here
|
||||
// These either have custom class names that can't be changed or have styles that need to be overridden
|
||||
//
|
||||
|
||||
// Code-viewer
|
||||
.CodeMirror {
|
||||
@extend .file-render__content;
|
||||
|
||||
.cm-invalidchar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.CodeMirror .CodeMirror-lines {
|
||||
// is there really a .CodeMirror inside a .CodeMirror?
|
||||
padding: var(--spacing-s) 0;
|
||||
}
|
||||
|
||||
.CodeMirror-code {
|
||||
@include font-sans;
|
||||
letter-spacing: 0.1rem;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
background-color: var(--color-gray-1);
|
||||
border-right: 1px solid var(--color-gray-4);
|
||||
padding-right: var(--spacing-m);
|
||||
}
|
||||
|
||||
.CodeMirror-line {
|
||||
padding-left: var(--spacing-m);
|
||||
}
|
||||
|
||||
.CodeMirror-linenumber {
|
||||
color: var(--color-gray-5);
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// Video
|
||||
// ****************************************************************************
|
||||
|
||||
.video-js-parent {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// By default no video js play button
|
||||
.vjs-big-play-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.vjs-modal-dialog .vjs-modal-dialog-content {
|
||||
position: relative;
|
||||
padding-top: 5rem;
|
||||
// Make sure no videojs message interferes with overlaying buttons
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.vjs-control-bar {
|
||||
// background-color: rgba(0, 0, 0, 0.8);
|
||||
|
||||
.vjs-remaining-time {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vjs-current-time,
|
||||
.vjs-time-divider,
|
||||
.vjs-duration {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-picture-in-picture-control {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// Video::Overlays
|
||||
// ****************************************************************************
|
||||
|
||||
.video-js {
|
||||
.vjs-overlay-playrate,
|
||||
.vjs-overlay-seeked {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
font-size: var(--font-large);
|
||||
width: auto;
|
||||
padding: 10px 30px;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
animation: fadeOutAnimation ease-in 0.6s;
|
||||
animation-iteration-count: 1;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeOutAnimation {
|
||||
0% {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// Video - Mobile UI
|
||||
// ****************************************************************************
|
||||
|
||||
.video-js.vjs-mobile-ui {
|
||||
.vjs-control-bar {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.vjs-touch-overlay:not(.show-play-toggle) {
|
||||
.vjs-control-bar {
|
||||
// Sync the controlBar's visibility with the overlay's
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-touch-overlay {
|
||||
&.show-play-toggle,
|
||||
&.skip {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
// Override the original's 'display: block' to avoid the big play button
|
||||
// from being squished to the side:
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
video::-internal-media-controls-overlay-cast-button {
|
||||
// Push the cast button above vjs-touch-overlay:
|
||||
z-index: 3;
|
||||
|
||||
// Move it to the upper-right since it will clash with "tap to unmute":
|
||||
left: unset;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.video-js.video-js.vjs-user-inactive {
|
||||
video::-internal-media-controls-overlay-cast-button {
|
||||
// (1) Android-Chrome's original Cast button behavior:
|
||||
// - If video is playing, fade out the button.
|
||||
// - If video is paused and video is tapped, display the button and stay on.
|
||||
// (2) We then injected another behavior:
|
||||
// - Display the button when '.vjs-touch-overlay' is displayed. However,
|
||||
// the 'controlslist' attribute hack that was used to do this results in the
|
||||
// button staying displayed without a fade-out timer.
|
||||
// (3) Ideally, we should sync the '.vjs-touch-overlay' visibility with the
|
||||
// cast button, similar to how to controlBar's visibility is synced above.
|
||||
// But I have no idea how to grab the sibling '.show-play-toggle' into the
|
||||
// css logic.
|
||||
// (4) So, this is the best that I can come up with: Whenever user is idle,
|
||||
// the button goes away. The only downside I know is the scenario of
|
||||
// "overlay is up and video is paused, but button goes away due to idle".
|
||||
// The user just needs to re-tap any empty space on the overlay to get the
|
||||
// Cast button again.
|
||||
opacity: 0;
|
||||
transition: opacity 1s ease;
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// Layout and control visibility
|
||||
// ****************************************************************************
|
||||
|
||||
.video-js.vjs-fullscreen,
|
||||
.video-js:not(.vjs-fullscreen) {
|
||||
// --- Unhide desired components per layout ---
|
||||
&.vjs-layout-x-small {
|
||||
.vjs-playback-rate {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.vjs-layout-small {
|
||||
.vjs-current-time,
|
||||
.vjs-time-divider,
|
||||
.vjs-duration,
|
||||
.vjs-playback-rate {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Adjust spacing ---
|
||||
.vjs-current-time {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.vjs-duration {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.vjs-playback-rate .vjs-playback-rate-value {
|
||||
// Reduce the gigantic font a bit. Default was 1.5em.
|
||||
font-size: 1.25em;
|
||||
line-height: 2.5;
|
||||
}
|
||||
|
||||
.vjs-playback-rate .vjs-menu {
|
||||
// Extend the width to prevent a potential scrollbar from blocking the text.
|
||||
width: 8em;
|
||||
left: -2em;
|
||||
}
|
||||
}
|
||||
|
||||
.video-js.vjs-fullscreen {
|
||||
.vjs-button--theater-mode {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// Tap-to-unmute
|
||||
// ****************************************************************************
|
||||
|
||||
.video-js--tap-to-unmute {
|
||||
visibility: hidden; // Start off as hidden.
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: var(--spacing-xs);
|
||||
left: var(--spacing-xs);
|
||||
padding: var(--spacing-xs) var(--spacing-m); // Make it comfy for touch.
|
||||
color: var(--color-gray-1);
|
||||
background: black;
|
||||
border: 1px solid var(--color-gray-4);
|
||||
opacity: 0.9;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
color: var(--color-white);
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
|
||||
.video-js:hover {
|
||||
.vjs-big-play-button {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.file-render {
|
||||
.video-js {
|
||||
/*display: flex;*/
|
||||
/*align-items: center;*/
|
||||
/*justify-content: center;*/
|
||||
}
|
||||
|
||||
.vjs-big-play-button {
|
||||
@extend .button--icon;
|
||||
@extend .button--play;
|
||||
border: none;
|
||||
/*position: static;*/
|
||||
z-index: 2;
|
||||
|
||||
.vjs-icon-placeholder {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-menu-item-text,
|
||||
.vjs-icon-placeholder {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
|
||||
.file-render--embed {
|
||||
// on embeds, do not inject our colors until interaction
|
||||
.video-js:hover {
|
||||
.vjs-big-play-button {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-paused {
|
||||
.vjs-big-play-button {
|
||||
display: block;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-ended {
|
||||
.vjs-big-play-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.video-js--tap-to-unmute {
|
||||
margin-top: var(--spacing-xl);
|
||||
margin-left: var(--spacing-s);
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
margin-left: calc(var(--spacing-m) + var(--spacing-s));
|
||||
}
|
||||
}
|
||||
|
||||
.file-viewer {
|
||||
iframe,
|
||||
webview,
|
||||
img {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-viewer--ended-embed .vjs-big-play-button {
|
||||
display: none !important; // yes this is dumb, but this was broken and the above CSS was overriding
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// Autoplay Countdown
|
||||
// ****************************************************************************
|
||||
|
||||
.autoplay-countdown {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.autoplay-countdown__timer {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: var(--font-small);
|
||||
}
|
||||
.autoplay-countdown__counter {
|
||||
margin-top: var(--spacing-m);
|
||||
}
|
||||
|
||||
.autoplay-countdown__button {
|
||||
/* Border size and color */
|
||||
border: 3px solid transparent;
|
||||
/* Creates a circle */
|
||||
border-radius: 50%;
|
||||
/* Circle size */
|
||||
display: inline-block;
|
||||
height: 86px;
|
||||
width: 86px;
|
||||
/* Use transform to rotate to adjust where opening appears */
|
||||
transition: border 1s;
|
||||
transform: rotate(45deg);
|
||||
.button {
|
||||
background-color: transparent;
|
||||
transform: rotate(-45deg);
|
||||
&:hover {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.autoplay-countdown__button--4 {
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.autoplay-countdown__button--3 {
|
||||
border-top-color: #fff;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
.autoplay-countdown__button--2 {
|
||||
border-top-color: #fff;
|
||||
border-right-color: #fff;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.autoplay-countdown__button--1 {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
|
||||
.file__viewdate {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
margin-bottom: var(--spacing-s);
|
||||
|
||||
> :first-child {
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-medium) {
|
||||
flex-direction: row;
|
||||
justify-content: start;
|
||||
|
||||
> :first-child {
|
||||
margin-bottom: 0;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-page__image {
|
||||
img {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
713
web/scss/themes/odysee/component/_form-field.scss
Normal file
713
web/scss/themes/odysee/component/_form-field.scss
Normal file
|
@ -0,0 +1,713 @@
|
|||
@import '../init/mixins';
|
||||
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
.date-picker-input {
|
||||
height: var(--height-input);
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid;
|
||||
color: var(--color-input);
|
||||
border-color: var(--color-input-border);
|
||||
background-color: var(--color-input-bg);
|
||||
padding-right: var(--spacing-s);
|
||||
padding-left: var(--spacing-s);
|
||||
|
||||
&:focus {
|
||||
@include focus;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-input-placeholder);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.4;
|
||||
|
||||
& + label {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
&[type='range'] {
|
||||
height: auto;
|
||||
height: 0.5rem;
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
checkbox-element,
|
||||
radio-element,
|
||||
select {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
background-image: var(--select-toggle-background);
|
||||
background-position: 99% center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1rem;
|
||||
padding-right: var(--spacing-l);
|
||||
padding-left: var(--spacing-s);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
fieldset-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
&.fieldset-group--smushed {
|
||||
fieldset-section + fieldset-section {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fieldset-section,
|
||||
fieldset-group,
|
||||
form,
|
||||
.checkbox,
|
||||
.radio,
|
||||
.form-field--SimpleMDE,
|
||||
.form-field__help {
|
||||
+ fieldset-section,
|
||||
+ fieldset-group,
|
||||
+ form,
|
||||
+ .checkbox,
|
||||
+ .radio,
|
||||
+ .form-field--SimpleMDE {
|
||||
margin-top: var(--spacing-l);
|
||||
}
|
||||
|
||||
+ .form-field__help {
|
||||
margin-top: var(--spacing-s);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset-section,
|
||||
.checkbox,
|
||||
.radio {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: var(--font-small);
|
||||
color: var(--color-input-label);
|
||||
display: inline-block;
|
||||
margin-bottom: 0.1rem;
|
||||
|
||||
.icon__lbc {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
input-submit {
|
||||
display: flex;
|
||||
|
||||
& > *:first-child,
|
||||
& > *:nth-child(2) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& > *:first-child {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
& > *:nth-child(2) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox,
|
||||
.radio {
|
||||
position: relative;
|
||||
|
||||
input[type='checkbox'],
|
||||
input[type='radio'] {
|
||||
height: var(--height-checkbox);
|
||||
width: var(--height-checkbox);
|
||||
position: absolute;
|
||||
border: none;
|
||||
left: 0;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
|
||||
&:disabled + label {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
font-size: var(--font-base);
|
||||
padding-left: calc(var(--height-checkbox) + var(--spacing-s));
|
||||
min-height: var(--height-checkbox);
|
||||
|
||||
&::before {
|
||||
background-color: var(--color-input-toggle-bg);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::before {
|
||||
background-color: var(--color-input-toggle-bg-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label::before,
|
||||
label::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
}
|
||||
|
||||
// Hide the checkmark by default
|
||||
input[type='checkbox'] + label::after,
|
||||
input[type='radio'] + label::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
// Unhide on the checked state
|
||||
input[type='checkbox']:checked + label::after,
|
||||
input[type='radio']:checked + label::after {
|
||||
content: '';
|
||||
}
|
||||
|
||||
input[type='checkbox']:focus + label::before,
|
||||
input[type='radio']:focus + label::before {
|
||||
@include focus;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
// Outer box of the fake checkbox
|
||||
label::before {
|
||||
height: var(--height-checkbox);
|
||||
width: var(--height-checkbox);
|
||||
border: 1px solid var(--color-input-border);
|
||||
border-radius: var(--border-radius);
|
||||
left: 0px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
// Checkmark of the fake checkbox
|
||||
label::after {
|
||||
height: 6px;
|
||||
width: 12px;
|
||||
border-left: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-color: var(--color-input-toggle);
|
||||
border-left-color: var(--color-input-toggle);
|
||||
transform: rotate(-45deg);
|
||||
left: 6px;
|
||||
top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.radio {
|
||||
input[type='radio'] {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
// Outer box of the fake radio
|
||||
label::before {
|
||||
height: var(--height-radio);
|
||||
width: var(--height-radio);
|
||||
border: 1px solid var(--color-input-border);
|
||||
border-radius: calc(var(--height-radio) * 0.5);
|
||||
left: 0px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
// Checkmark of the fake radio
|
||||
label::after {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-primary);
|
||||
left: 6px;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.range__label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-bottom: var(--spacing-m);
|
||||
|
||||
> * {
|
||||
width: 33%;
|
||||
text-align: center;
|
||||
|
||||
&:first-of-type {
|
||||
text-align: left;
|
||||
}
|
||||
&:last-of-type {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fieldset-group {
|
||||
@extend fieldset-group;
|
||||
}
|
||||
|
||||
.fieldset-section {
|
||||
@extend fieldset-section;
|
||||
}
|
||||
|
||||
.input-submit {
|
||||
@extend input-submit;
|
||||
}
|
||||
|
||||
input-submit {
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
input[type='number'] {
|
||||
width: 8rem;
|
||||
}
|
||||
|
||||
fieldset-group {
|
||||
+ fieldset-group {
|
||||
margin-top: var(--spacing-s);
|
||||
}
|
||||
|
||||
&.fieldset-group--smushed {
|
||||
justify-content: flex-start;
|
||||
|
||||
fieldset-section {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
|
||||
&:first-child {
|
||||
input,
|
||||
select {
|
||||
border-right: none;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-of-type(2) {
|
||||
input,
|
||||
select {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-left: var(--spacing-s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.fieldgroup--paginate {
|
||||
padding-bottom: var(--spacing-l);
|
||||
margin-top: var(--spacing-l);
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a special case where the prefix appears "inside" the input
|
||||
// It would be way simpler to just use position: absolute and give it a width
|
||||
// but the width can change when we use it for the name prefix
|
||||
// lbry:// {input}, lbry://@short {input}, @lbry://longername {input}
|
||||
// The spacing/alignment isn't very robust and will probably need to be changed
|
||||
// if we use this in more places
|
||||
&.fieldset-group--disabled-prefix {
|
||||
align-items: flex-end;
|
||||
|
||||
label {
|
||||
min-height: 18px;
|
||||
white-space: nowrap;
|
||||
// Set width 0 and overflow visible so the label can act as if it's the input label and not a random text node in a side by side div
|
||||
overflow: visible;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
fieldset-section:first-child {
|
||||
max-width: 40%;
|
||||
|
||||
.form-field__prefix {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
padding: 0.5rem;
|
||||
height: var(--height-input);
|
||||
border: 1px solid;
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
border-color: var(--color-input-border);
|
||||
border-right-color: var(--color-input-prefix-border);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-input-prefix-bg);
|
||||
}
|
||||
}
|
||||
|
||||
fieldset-section:last-child {
|
||||
width: 100%;
|
||||
|
||||
label {
|
||||
// Overwrite the input's label to wrap instead. This is usually
|
||||
// an error message, which could be long in other languages.
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
input {
|
||||
border-left: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-color: var(--color-input-border);
|
||||
padding-left: var(--spacing-xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-field--copyable {
|
||||
padding: 0.2rem 0.75rem;
|
||||
text-overflow: ellipsis;
|
||||
user-select: text;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.form-field--short {
|
||||
width: 100%;
|
||||
@media (min-width: $breakpoint-small) {
|
||||
width: 25em;
|
||||
}
|
||||
}
|
||||
|
||||
.form-field--price-amount {
|
||||
max-width: 6em;
|
||||
}
|
||||
|
||||
.form-field--price-amount--auto {
|
||||
width: auto;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.form-field--address {
|
||||
min-width: 18em;
|
||||
@media (max-width: $breakpoint-xxsmall) {
|
||||
min-width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
.form-field__help {
|
||||
@extend .help;
|
||||
}
|
||||
|
||||
.form-field__help + .checkbox,
|
||||
.form-field__help + .radio {
|
||||
margin-top: var(--spacing-l);
|
||||
}
|
||||
|
||||
.form-field__conjuction {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.form-field__two-column {
|
||||
@media (min-width: $breakpoint-small) {
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.form-field__quick-action {
|
||||
float: right;
|
||||
font-size: var(--font-xsmall);
|
||||
margin-top: 2.5%;
|
||||
}
|
||||
|
||||
.form-field__textarea-info {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: var(--spacing-xxs);
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
.form-field__quick-emojis {
|
||||
> *:not(:last-child) {
|
||||
margin-right: var(--spacing-s);
|
||||
}
|
||||
}
|
||||
|
||||
fieldset-section {
|
||||
.form-field__internal-option {
|
||||
margin-top: var(--spacing-s);
|
||||
margin-left: 2.2rem;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: var(--spacing-s); // Extra specificity needed here since _section.scss is applied after this file
|
||||
}
|
||||
}
|
||||
|
||||
.select--slim {
|
||||
margin-bottom: var(--spacing-xxs);
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
select {
|
||||
max-height: 1.5rem !important;
|
||||
padding: 0 var(--spacing-xs);
|
||||
padding-right: var(--spacing-l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#automatic_dark_mode_range_start,
|
||||
#automatic_dark_mode_range_end {
|
||||
min-width: 6em;
|
||||
}
|
||||
|
||||
.date-picker-input {
|
||||
font-weight: bold;
|
||||
|
||||
.react-datetime-picker__wrapper {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.form-field-date-picker {
|
||||
margin-bottom: var(--spacing-l);
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
|
||||
.date-picker-input,
|
||||
.button--link {
|
||||
margin-right: var(--spacing-m);
|
||||
}
|
||||
}
|
||||
|
||||
.react-datetime-picker__button {
|
||||
svg {
|
||||
stroke: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.react-datetime-picker__button:enabled:hover .react-datetime-picker__button__icon,
|
||||
.react-datetime-picker__button:enabled:focus .react-datetime-picker__button__icon {
|
||||
stroke: var(--color-primary);
|
||||
}
|
||||
|
||||
.react-date-picker__calendar {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.react-calendar {
|
||||
width: 350px;
|
||||
max-width: 100%;
|
||||
background: var(--color-card-background);
|
||||
border: 1px solid #a0a096;
|
||||
font-family: inherit;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.react-calendar--doubleView {
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
.react-calendar--doubleView .react-calendar__viewContainer {
|
||||
display: flex;
|
||||
margin: -0.5em;
|
||||
}
|
||||
|
||||
.react-calendar--doubleView .react-calendar__viewContainer > * {
|
||||
width: 50%;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.react-calendar,
|
||||
.react-calendar *,
|
||||
.react-calendar *:before,
|
||||
.react-calendar *:after {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 2px 1px;
|
||||
}
|
||||
|
||||
.react-calendar button {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.react-calendar button:enabled:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.react-calendar__navigation {
|
||||
height: 44px;
|
||||
margin-bottom: 1em;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.react-calendar__navigation__label {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.react-calendar__navigation button {
|
||||
min-width: 44px;
|
||||
background: none;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.react-calendar__navigation button:enabled:hover,
|
||||
.react-calendar__navigation button:enabled:focus {
|
||||
background: var(--color-button-alt-bg-hover);
|
||||
}
|
||||
|
||||
.react-calendar__navigation button[disabled] {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.react-calendar__month-view__weekdays {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
font-size: 0.75em;
|
||||
color: var(--color-text-alt);
|
||||
}
|
||||
|
||||
.react-calendar__month-view__weekdays__weekday {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.react-calendar__month-view__weekNumbers {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.react-calendar__month-view__weekNumbers .react-calendar__tile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.75em;
|
||||
padding: calc(0.75em / 0.75) calc(0.5em / 0.75);
|
||||
}
|
||||
|
||||
.react-calendar__month-view__days__day,
|
||||
.react-calendar__month-view__days__day--weekend {
|
||||
color: var(--color-text);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.react-calendar__month-view__days__day--neighboringMonth {
|
||||
color: var(--color-gray-5);
|
||||
}
|
||||
|
||||
.react-calendar__year-view .react-calendar__tile,
|
||||
.react-calendar__decade-view .react-calendar__tile,
|
||||
.react-calendar__century-view .react-calendar__tile {
|
||||
padding: 2em 0.5em;
|
||||
}
|
||||
|
||||
.react-calendar__tile {
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
padding: 0.75em 0.5em;
|
||||
background: none;
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.react-calendar__tile:enabled:hover,
|
||||
.react-calendar__tile:enabled:focus {
|
||||
background: var(--color-button-alt-bg-hover);
|
||||
}
|
||||
|
||||
.react-calendar__tile--now {
|
||||
background: var(--color-button-secondary-bg);
|
||||
}
|
||||
|
||||
.react-calendar__tile--now:enabled:hover,
|
||||
.react-calendar__tile--now:enabled:focus {
|
||||
background: var(--color-button-secondary-bg-hover);
|
||||
}
|
||||
|
||||
.react-calendar__tile--hasActive {
|
||||
color: var(--color-button-primary-text);
|
||||
background: var(--color-button-primary-bg);
|
||||
}
|
||||
|
||||
.react-calendar__tile--hasActive:enabled:hover,
|
||||
.react-calendar__tile--hasActive:enabled:focus {
|
||||
background: var(--color-button-primary-bg-hover);
|
||||
}
|
||||
|
||||
.react-calendar__tile--active {
|
||||
color: var(--color-button-primary-text);
|
||||
background: var(--color-button-primary-bg);
|
||||
}
|
||||
|
||||
.react-calendar__tile--active:enabled:hover,
|
||||
.react-calendar__tile--active:enabled:focus {
|
||||
background: var(--color-button-primary-bg-hover);
|
||||
}
|
||||
|
||||
.react-calendar--selectRange .react-calendar__tile--hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.react-datetime-picker__inputGroup__amPm {
|
||||
background: var(--color-input-bg);
|
||||
}
|
||||
|
||||
.react-datetime-picker__inputGroup__leadingZero {
|
||||
// Not perfect, but good enough for our standard zoom levels.
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.react-datetime-picker__inputGroup__input--hasLeadingZero {
|
||||
margin-left: -0.54em;
|
||||
padding-left: calc(1px + 0.54em);
|
||||
}
|
||||
|
||||
.react-calendar__month-view__days__day--neighboringMonth {
|
||||
color: var(--color-gray-5);
|
||||
}
|
||||
}
|
||||
|
||||
.form-field-calendar {
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--color-border);
|
||||
margin-left: calc(var(--spacing-xs) * -1);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
animation: menu-animate-in var(--animation-duration) var(--animation-style);
|
||||
box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
143
web/scss/themes/odysee/dark.scss
Normal file
143
web/scss/themes/odysee/dark.scss
Normal file
|
@ -0,0 +1,143 @@
|
|||
[theme='dark'] {
|
||||
// Color overrides
|
||||
--color-primary: #e50054;
|
||||
--color-primary-alt: #66001880;
|
||||
--color-fire: #ff6635;
|
||||
--color-fire-outside: #ff9b20;
|
||||
|
||||
// Structure
|
||||
--color-background: #140e1b;
|
||||
--color-background-overlay: #0c0d0e95;
|
||||
--color-border: #30243d;
|
||||
--color-card-background: #181021;
|
||||
--color-card-background-highlighted: #241c30;
|
||||
|
||||
// Text
|
||||
--color-text: var(--color-gray-1);
|
||||
--color-text-subtitle: var(--color-gray-4);
|
||||
--color-text-empty: var(--color-text-subtitle);
|
||||
--color-text-help: #bbbbbb;
|
||||
--color-text-warning: #212529;
|
||||
--color-text-warning--background: var(--lbry-yellow-1);
|
||||
--color-text-error: var(--color-danger);
|
||||
--color-error: var(--color-danger-alt);
|
||||
--color-blockquote: var(--color-gray-5);
|
||||
--color-blockquote-bg: var(--color-card-background-highlighted);
|
||||
--color-help-warning-text: var(--color-white-alt);
|
||||
--color-help-warning-bg: #fbbf2450;
|
||||
|
||||
// Tags (words)
|
||||
--color-tag-words: var(--color-text);
|
||||
--color-tag-words-bg: var(--color-gray-5);
|
||||
--color-tag-words-hover: var(--color-white);
|
||||
--color-tag-words-bg-hover: var(--color-gray-4);
|
||||
|
||||
// Header
|
||||
--color-header-button: #38274c;
|
||||
--color-header-background: #231830;
|
||||
|
||||
// Button
|
||||
--color-button-primary-text: white;
|
||||
--color-button-primary-hover-text: var(--color-primary-alt);
|
||||
--color-button-secondary-bg: #2c1543;
|
||||
--color-button-secondary-border: #4f1c82;
|
||||
--color-button-secondary-bg-hover: #3b1c5b;
|
||||
--color-button-secondary-text: #efefef;
|
||||
--color-button-alt-bg: var(--color-header-button);
|
||||
--color-button-alt-bg-hover: #2b2037;
|
||||
--color-button-toggle-text: var(--color-text);
|
||||
--color-button-toggle-bg: var(--color-primary-alt);
|
||||
--color-button-toggle-bg-hover: var(--color-primary-alt);
|
||||
--color-button-alt-text: #e2e9f0;
|
||||
--color-button-border: #5b4475;
|
||||
--color-link: var(--color-primary);
|
||||
--color-link-hover: #d75673;
|
||||
--color-link-active: #ec1d4c;
|
||||
--color-link-focus-bg: #3d2d4e;
|
||||
|
||||
// Input
|
||||
--color-input: #f4f4f5;
|
||||
--color-input-label: #a7a7a7;
|
||||
--color-input-placeholder: #f4f4f5;
|
||||
--color-input-bg: var(--color-header-button);
|
||||
--color-input-bg-copyable: #4c3861;
|
||||
--color-input-border: var(--color-border);
|
||||
--color-input-border-active: var(--color-secondary);
|
||||
--color-input-toggle: var(--color-primary-alt-3);
|
||||
--color-input-toggle-bg: var(--color-input-bg);
|
||||
--color-input-toggle-bg-hover: var(--color-secondary);
|
||||
--color-input-bg-selected: var(--color-primary-alt);
|
||||
--color-input-prefix-bg: var(--color-input-bg-copyable);
|
||||
--color-input-prefix-border: var(--color-gray-4);
|
||||
--select-toggle-background: url("data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg' fill='%23ffffff'%3E%3Cpath d='M17.172, 31.172c1.562, -1.562 4.095, -1.562 5.656, 0l25.172, 25.171l25.172, -25.171c1.562, -1.562 4.095, -1.562 5.656, 0c1.562, 1.562 1.562, 4.095 0, 5.656l-28, 28c-1.562, 1.562 -4.095, 1.562 -5.656, 0l-28, -28c-0.781, -0.781 -1.172, -1.805 -1.172, -2.828c0, -1.023 0.391, -2.047 1.172, -2.828Z'/%3E%3C/svg%3E%0A");
|
||||
|
||||
// Navigation
|
||||
--color-navigation-icon: #76808a;
|
||||
--color-navigation-link: #b9c3ce;
|
||||
--color-navigation-active: #2b2037;
|
||||
--color-navigation-active-text: #c6bcd2;
|
||||
--color-navigation-hover: #21182a;
|
||||
--color-navigation-hover-text: #c6bcd2;
|
||||
|
||||
// Tags
|
||||
--color-tag: #ff85b1;
|
||||
--color-tag-bg: var(--color-navigation-hover);
|
||||
--color-tag-hover: var(--color-white);
|
||||
--color-tag-bg-hover: var(--color-primary-alt-2);
|
||||
--color-tag-mature-bg: var(--color-primary-alt-2);
|
||||
|
||||
// Menu
|
||||
--color-menu-background: var(--color-header-background);
|
||||
--color-menu-background--active: var(--color-primary-alt);
|
||||
--color-menu-icon: #928b9b;
|
||||
--color-menu-icon-active: #d6d6d6;
|
||||
|
||||
// Comments
|
||||
--color-comment-menu: #6a6a6a;
|
||||
--color-comment-menu-hovering: #e0e0e0;
|
||||
--color-comment-highlighted: #484734;
|
||||
--color-comment-threadline: #24192f;
|
||||
--color-comment-threadline-hover: var(--color-gray-4);
|
||||
|
||||
// Other
|
||||
--color-tab-text: var(--color-white);
|
||||
--color-tabs-background: var(--color-card-background);
|
||||
--color-tab-divider: var(--color-white);
|
||||
--color-modal-background: var(--color-card-background);
|
||||
--color-notice: #58563b;
|
||||
--color-purchased: #ffd580;
|
||||
--color-purchased-alt: var(--color-purchased);
|
||||
--color-purchased-text: black;
|
||||
--color-thumbnail-background: var(--color-gray-5);
|
||||
--color-tooltip-bg: #2f3337;
|
||||
--color-focus: #e91e6329;
|
||||
--color-placeholder-background: #261a35;
|
||||
--color-spinner-light: white;
|
||||
--color-spinner-dark: #212529;
|
||||
--color-login-graphic-background: var(--color-background);
|
||||
|
||||
// Editor
|
||||
--color-editor-cursor: var(--color-text);
|
||||
--color-editor-quote: #d3d3d3;
|
||||
--color-editor-tag: #efbe5d;
|
||||
--color-editor-attr: #68ccf9;
|
||||
--color-editor-string: #ff8b6b;
|
||||
--color-editor-inline-code-fg: #ce9178;
|
||||
--color-editor-inline-code-fg-preview: #e8b692;
|
||||
--color-editor-inline-code-bg: rgba(20, 68, 102, 0.3);
|
||||
--color-editor-inline-code-bg-preview: #464b50;
|
||||
--color-editor-selected: #264f78;
|
||||
--color-editor-link: var(--color-link);
|
||||
--color-editor-url: var(--color-editor-string);
|
||||
--color-editor-hr: var(--color-editor-tag);
|
||||
--color-editor-hr-preview: #a0a0a0;
|
||||
|
||||
// Ads
|
||||
--color-ads-background: #475057;
|
||||
--color-ads-text: #111;
|
||||
--color-ads-link: var(--color-primary-alt);
|
||||
|
||||
// Scrollbar
|
||||
--color-scrollbar-thumb-bg: rgba(255, 255, 255, 0.2);
|
||||
--color-scrollbar-track-bg: transparent;
|
||||
}
|
199
web/scss/themes/odysee/init/_base-theme.scss
Normal file
199
web/scss/themes/odysee/init/_base-theme.scss
Normal file
|
@ -0,0 +1,199 @@
|
|||
//
|
||||
// Colors are taken from this color palette
|
||||
// https://tailwindcss.com/docs/customizing-colors
|
||||
// New colors should be also taken from the same color palette (if possible)
|
||||
//
|
||||
:root {
|
||||
// Generic colors
|
||||
--color-primary: #047857;
|
||||
--color-primary-alt: #e4f4ef;
|
||||
--color-primary-alt-2: #065f46;
|
||||
--color-primary-alt-3: #10b981;
|
||||
--color-secondary: #1e3a8a;
|
||||
--color-secondary-alt: #dbeafe;
|
||||
--color-secondary-alt-2: #bfdbfe;
|
||||
--color-secondary-alt-3: #1e40af;
|
||||
--color-tertiary: #5b21b6;
|
||||
--color-tertiary-alt: #f5f3ff;
|
||||
--color-danger: #991b1b;
|
||||
--color-danger-alt: #fecaca;
|
||||
--color-warning: #fff58c;
|
||||
--color-black: #111;
|
||||
--color-white: #fdfdfd;
|
||||
--color-white-alt: #fafafa;
|
||||
--color-gray-1: #f3f4f6;
|
||||
--color-gray-2: #e5e7eb;
|
||||
--color-gray-3: #d1d5db;
|
||||
--color-gray-4: #9ca3af;
|
||||
--color-gray-5: #71717a;
|
||||
--color-gray-6: #52525b;
|
||||
--color-gray-7: #3f3f46;
|
||||
--color-gray-8: #27272a;
|
||||
--color-gray-9: #1f1f22;
|
||||
--color-gray-10: #18181b;
|
||||
--color-amber: #f26522;
|
||||
--color-orange: #fb923c;
|
||||
|
||||
// Structure
|
||||
--color-text: var(--color-black);
|
||||
--color-text-inverse: #fdfdfd;
|
||||
--color-background: #fafafa;
|
||||
--color-background--splash: #212529;
|
||||
--color-border: #ededed;
|
||||
--color-background-overlay: #21252980;
|
||||
--color-card-background: #ffffff;
|
||||
--color-card-background-highlighted: #f1f7fe;
|
||||
|
||||
// Text
|
||||
--color-text-selection-bg: var(--color-secondary-alt);
|
||||
--color-text-selection: var(--color-secondary);
|
||||
--color-text-empty: #999999;
|
||||
--color-text-help: #999999;
|
||||
--color-text-subtitle: #767676;
|
||||
--color-text-warning: #212529;
|
||||
--color-help-warning-bg: #fef3c7;
|
||||
--color-help-warning-text: #555555;
|
||||
--color-text-warning--background: var(--lbry-yellow-1);
|
||||
--color-blockquote: var(--color-gray-3);
|
||||
--color-text-error: var(--color-danger);
|
||||
--color-error: var(--color-danger-alt);
|
||||
--color-tooltip-bg: #222;
|
||||
--color-tooltip-text: #fafafa;
|
||||
|
||||
// Header
|
||||
--color-header-background: #ffffff;
|
||||
--color-header-button: var(--color-button-alt-bg);
|
||||
--color-header-button-active: var(--color-primary-alt);
|
||||
--color-header-button-hover: var(--color-primary-alt);
|
||||
|
||||
// Button
|
||||
--color-button-primary-bg: var(--color-primary);
|
||||
--color-button-primary-text: var(--color-primary-alt);
|
||||
--color-button-primary-bg-hover: var(--color-primary-alt-2);
|
||||
--color-button-primary-hover-text: var(--color-primary-alt);
|
||||
--color-button-secondary-bg: var(--color-secondary-alt);
|
||||
--color-button-secondary-border: var(--color-secondary-alt);
|
||||
--color-button-secondary-text: var(--color-secondary);
|
||||
--color-button-secondary-bg-hover: var(--color-secondary-alt-2);
|
||||
--color-button-alt-bg: var(--color-gray-1);
|
||||
--color-button-alt-text: var(--color-text);
|
||||
--color-button-alt-bg-hover: var(--color-gray-2);
|
||||
--color-button-toggle-text: var(--color-primary);
|
||||
--color-button-toggle-bg: var(--color-primary-alt);
|
||||
--color-button-border: var(--color-gray-3);
|
||||
--color-link: var(--color-primary);
|
||||
--color-link-hover: var(--color-black);
|
||||
--color-link-focus-bg: var(--color-gray-1);
|
||||
|
||||
// Input
|
||||
--color-input-color: var(--color-black);
|
||||
--color-input-label: var(--color-gray-5);
|
||||
--color-input-placeholder: var(--color-gray-8);
|
||||
--color-input-bg: var(--color-gray-1);
|
||||
--color-input-border: var(--color-border);
|
||||
--color-input-border-active: var(--color-secondary);
|
||||
--color-input-toggle: var(--color-secondary);
|
||||
--color-input-toggle-bg: var(--color-gray-1);
|
||||
--color-input-toggle-bg-hover: var(--color-secondary-alt);
|
||||
--color-input-prefix-bg: var(--color-gray-2);
|
||||
--color-input-prefix-border: var(--color-gray-5);
|
||||
--color-input-bg-selected: var(--color-primary-alt);
|
||||
--select-toggle-background: url("data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg' fill='%23212529'%3E%3Cpath d='M17.172, 31.172c1.562, -1.562 4.095, -1.562 5.656, 0l25.172, 25.171l25.172, -25.171c1.562, -1.562 4.095, -1.562 5.656, 0c1.562, 1.562 1.562, 4.095 0, 5.656l-28, 28c-1.562, 1.562 -4.095, 1.562 -5.656, 0l-28, -28c-0.781, -0.781 -1.172, -1.805 -1.172, -2.828c0, -1.023 0.391, -2.047 1.172, -2.828Z'/%3E%3C/svg%3E%0A");
|
||||
|
||||
// Navigation
|
||||
--color-navigation-icon: var(--color-gray-5);
|
||||
--color-navigation-link: var(--color-gray-5);
|
||||
--color-navigation-active: var(--color-primary-alt);
|
||||
--color-navigation-active-text: var(--color-primary);
|
||||
--color-navigation-hover: var(--color-gray-1);
|
||||
--color-navigation-hover-text: var(--color-primary);
|
||||
|
||||
// Tags
|
||||
--color-tag: var(--color-gray-5);
|
||||
--color-tag-bg: var(--color-button-alt-bg);
|
||||
--color-tag-hover: var(--color-primary-alt);
|
||||
--color-tag-bg-hover: var(--color-button-primary-bg);
|
||||
|
||||
// Tags (words)
|
||||
--color-tag-words: var(--color-gray-5);
|
||||
--color-tag-words-bg: var(--color-button-alt-bg);
|
||||
--color-tag-words-hover: var(--color-button-alt-text);
|
||||
--color-tag-words-bg-hover: var(--color-button-alt-bg-hover);
|
||||
|
||||
// Menu
|
||||
--color-menu-background: var(--color-header-background);
|
||||
--color-menu-background--active: var(--color-card-background-highlighted);
|
||||
--color-menu-icon: var(--color-navigation-link);
|
||||
|
||||
// Comments
|
||||
--color-comment-menu: var(--color-gray-3);
|
||||
--color-comment-menu-hovering: var(--color-gray-6);
|
||||
--color-comment-highlighted: #fff2d9;
|
||||
--color-comment-threadline: var(--color-gray-1);
|
||||
--color-comment-threadline-hover: var(--color-gray-4);
|
||||
|
||||
// Icons
|
||||
--color-follow-bg: #ffd4da;
|
||||
--color-follow-icon: #e2495e;
|
||||
--color-view-bg: var(--color-secondary-alt);
|
||||
--color-view-icon: var(--color-secondary);
|
||||
|
||||
// Snack
|
||||
--color-snack-bg: var(--color-primary);
|
||||
--color-snack: var(--color-white);
|
||||
--color-snack-bg-error: var(--color-danger);
|
||||
--color-snack-upgrade: var(--color-tertiary);
|
||||
|
||||
// Superchat
|
||||
--color-superchat-text: var(--color-black);
|
||||
--color-superchat: var(--color-cost);
|
||||
--color-superchat__light: #fcd34d50;
|
||||
--color-superchat-2: #fde68a;
|
||||
--color-superchat-3: #fef3c7;
|
||||
--color-superchat-3__light: #fef3c750;
|
||||
--color-superchat-4: #fffbeb;
|
||||
|
||||
// Editor
|
||||
--color-editor-cursor: var(--color-text);
|
||||
--color-editor-quote: #707070;
|
||||
--color-editor-tag: #ea9400;
|
||||
--color-editor-attr: #04b0f4;
|
||||
--color-editor-string: #ff7451;
|
||||
--color-editor-inline-code-fg: var(--color-text);
|
||||
--color-editor-inline-code-fg-preview: #2e3439;
|
||||
--color-editor-inline-code-bg: rgba(157, 161, 165, 0.3);
|
||||
--color-editor-inline-code-bg-preview: #d0e8ff;
|
||||
--color-editor-selected: #add6ff;
|
||||
--color-editor-link: var(--color-link);
|
||||
--color-editor-url: var(--color-editor-string);
|
||||
--color-editor-hr: var(--color-editor-tag);
|
||||
--color-editor-hr-preview: #cccccc;
|
||||
|
||||
// Other
|
||||
--color-focus: #bfdbfe;
|
||||
--color-notification: #cc190f;
|
||||
--color-live: #cc190f;
|
||||
--color-nag: var(--color-orange);
|
||||
--color-cost: #fcd34d;
|
||||
--color-notice: #fef3ca;
|
||||
--color-purchased: var(--color-cost);
|
||||
--color-purchased-alt: #ffebc2;
|
||||
--color-purchased-text: var(--color-gray-5);
|
||||
--color-thumbnail-background: var(--color-gray-1);
|
||||
--color-spinner-light: var(--color-white);
|
||||
--color-spinner-dark: var(--color-black);
|
||||
--color-placeholder-background: var(--color-gray-1);
|
||||
--color-file-viewer-background: var(--color-card-background);
|
||||
--color-tabs-background: var(--color-card-background);
|
||||
--color-tab-divider: var(--color-primary);
|
||||
--color-modal-background: var(--color-card-background);
|
||||
--color-login-graphic-background: var(--color-primary-alt);
|
||||
|
||||
// Ads
|
||||
--color-ads-background: #fae5ff;
|
||||
--color-ads-link: var(--color-link);
|
||||
|
||||
// Scrollbar
|
||||
--color-scrollbar-thumb-bg: rgba(0, 0, 0, 0.2);
|
||||
--color-scrollbar-track-bg: transparent;
|
||||
}
|
57
web/scss/themes/odysee/init/_color.scss
Normal file
57
web/scss/themes/odysee/init/_color.scss
Normal file
|
@ -0,0 +1,57 @@
|
|||
:root {
|
||||
// Generic colors
|
||||
--color-primary: #257761;
|
||||
--color-primary-alt: #e4f4ef;
|
||||
--color-primary-alt-2: #4b8576;
|
||||
--color-secondary: #295284;
|
||||
--color-secondary-alt: #d9eaff;
|
||||
--color-tertiary: #552470;
|
||||
--color-tertiary-alt: #f7e8ff;
|
||||
--color-danger: #9b2023;
|
||||
--color-danger-alt: #fccdce;
|
||||
--color-warning: #fff58c;
|
||||
--color-cost: #ffd580;
|
||||
--color-focus: #93cff2;
|
||||
--color-notification: #f02849;
|
||||
--color-live: #cc190f;
|
||||
|
||||
--color-black: #111;
|
||||
--color-white: #fdfdfd;
|
||||
--color-white-alt: #fafafa;
|
||||
--color-gray-1: #eff1f4;
|
||||
--color-gray-2: #d8dde1;
|
||||
--color-gray-3: #ced4da;
|
||||
--color-gray-4: #abb1b7;
|
||||
--color-gray-5: #666a6d;
|
||||
|
||||
// Text
|
||||
--color-text: var(--color-black);
|
||||
--color-text-subtitle: var(--color-gray-5);
|
||||
--color-text-inverse: #fdfdfd;
|
||||
|
||||
// Components
|
||||
|
||||
// Button
|
||||
--color-button-primary-bg: var(--color-primary);
|
||||
--color-button-primary-text: var(--color-primary-alt);
|
||||
--color-button-primary-bg-hover: var(--color-primary-alt-2);
|
||||
--color-button-primary-hover-text: var(--color-primary-alt);
|
||||
--color-button-secondary-bg: var(--color-secondary-alt);
|
||||
--color-button-secondary-border: var(--color-secondary-alt);
|
||||
--color-button-secondary-text: var(--color-secondary);
|
||||
--color-button-secondary-bg-hover: #b9d0e9;
|
||||
--color-button-alt-bg: var(--color-gray-1);
|
||||
--color-button-alt-text: var(--color-text);
|
||||
--color-button-alt-bg-hover: var(--color-gray-2);
|
||||
--color-link: var(--color-primary);
|
||||
--color-link-hover: var(--color-black);
|
||||
|
||||
// Table
|
||||
--color-table-highlight: var(--color-white-alt);
|
||||
|
||||
// Tag
|
||||
--color-tag: var(--color-gray-5);
|
||||
--color-tag-bg: var(--color-button-alt-bg);
|
||||
--color-tag-hover: var(--color-button-alt-text);
|
||||
--color-tag-bg-hover: var(--color-button-alt-bg-hover);
|
||||
}
|
517
web/scss/themes/odysee/init/_gui.scss
Normal file
517
web/scss/themes/odysee/init/_gui.scss
Normal file
|
@ -0,0 +1,517 @@
|
|||
// Generic html styles used across the App
|
||||
// component specific styling should go in the component scss file
|
||||
|
||||
*::selection {
|
||||
background-color: var(--color-text-selection-bg);
|
||||
color: var(--color-text-selection);
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
html {
|
||||
@include font-sans;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
overflow-x: hidden;
|
||||
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-background);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 1em;
|
||||
cursor: default;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background-color: var(--color-gray-2);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p,
|
||||
ol,
|
||||
ul {
|
||||
& + p,
|
||||
& + ul,
|
||||
& + ol {
|
||||
margin-top: var(--spacing-s);
|
||||
}
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
li {
|
||||
position: relative;
|
||||
list-style-position: outside;
|
||||
margin: var(--spacing-xs) 0;
|
||||
margin-left: var(--spacing-s);
|
||||
margin-bottom: 0;
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
margin-left: var(--spacing-xl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ul--no-style {
|
||||
list-style: none;
|
||||
margin-bottom: 0;
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
dl {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
overflow-x: visible;
|
||||
margin-top: var(--spacing-m);
|
||||
}
|
||||
|
||||
dt {
|
||||
flex-basis: 50%;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dt__text {
|
||||
margin-right: var(--spacing-s);
|
||||
}
|
||||
|
||||
dd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-basis: 45%;
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
|
||||
.help--warning {
|
||||
margin-bottom: 0;
|
||||
margin-top: var(--spacing-s);
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.dd__text {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.dd__button {
|
||||
margin-right: var(--spacing-s);
|
||||
}
|
||||
|
||||
dt,
|
||||
dd {
|
||||
padding: var(--spacing-m) var(--spacing-s);
|
||||
border-top: 1px solid var(--color-border);
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: 0 0.8rem;
|
||||
margin-top: var(--spacing-xxs);
|
||||
margin-bottom: var(--spacing-xxs);
|
||||
opacity: 0.9;
|
||||
border-left: 0.2rem solid var(--color-blockquote);
|
||||
color: var(--color-text-subtitle);
|
||||
}
|
||||
|
||||
code {
|
||||
@include font-mono;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--color-border);
|
||||
}
|
||||
|
||||
img,
|
||||
a {
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
|
||||
img {
|
||||
// Hide alt text when an image fails to load
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: calc(var(--height-input) * 2);
|
||||
}
|
||||
|
||||
.columns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
|
||||
> * {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
min-width: 15rem;
|
||||
margin-bottom: var(--spacing-l);
|
||||
|
||||
&:first-child {
|
||||
flex-basis: 1px;
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
flex-direction: column;
|
||||
|
||||
& > * {
|
||||
margin: 0;
|
||||
margin-bottom: var(--spacing-m);
|
||||
width: 100%;
|
||||
flex-basis: auto;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
|
||||
.column__item:not(:first-child) {
|
||||
padding-left: $spacing-width * 2/3;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.column__item--between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
flex-direction: column;
|
||||
.column__item:not(:first-child) {
|
||||
padding-left: 0;
|
||||
flex: 1;
|
||||
}
|
||||
& > * {
|
||||
margin: 0;
|
||||
margin-bottom: var(--spacing-m);
|
||||
width: 100%;
|
||||
flex-basis: auto;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.truncated-text {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.busy-indicator__loader {
|
||||
min-width: 16px;
|
||||
min-height: 8px;
|
||||
margin: -1rem 0;
|
||||
padding: 0 30px;
|
||||
|
||||
background: url('../../static/img/busy.gif') no-repeat center center;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.help {
|
||||
display: block;
|
||||
font-size: var(--font-xsmall);
|
||||
color: var(--color-text-help);
|
||||
margin-top: var(--spacing-s);
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
|
||||
.button--link + .button--link {
|
||||
margin-left: var(--spacing-s);
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
font-size: var(--font-small);
|
||||
}
|
||||
}
|
||||
|
||||
.help--warning {
|
||||
@extend .help;
|
||||
padding: var(--spacing-s);
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--color-help-warning-bg);
|
||||
color: var(--color-help-warning-text);
|
||||
margin-bottom: var(--spacing-s);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.help--notice {
|
||||
@extend .help--warning;
|
||||
background-color: var(--color-card-background-highlighted);
|
||||
}
|
||||
|
||||
.help--inline {
|
||||
@extend .help;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.icon--help {
|
||||
top: 3px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.help--card-actions {
|
||||
@extend .help;
|
||||
margin-top: var(--spacing-m);
|
||||
}
|
||||
|
||||
.help--dt {
|
||||
@extend .help;
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.help--spendable {
|
||||
margin-left: var(--spacing-xxs);
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: var(--color-text-empty);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.empty--centered {
|
||||
text-align: center;
|
||||
padding: calc(var(--spacing-l) * 3) 0;
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
width: 134px;
|
||||
height: 134px;
|
||||
border: 3px solid white;
|
||||
|
||||
&.qr-code--right-padding {
|
||||
margin-right: $spacing-vertical * 2/3;
|
||||
}
|
||||
|
||||
&.qr-code--top-padding {
|
||||
margin-top: $spacing-vertical * 2/3;
|
||||
}
|
||||
}
|
||||
|
||||
.error__wrapper {
|
||||
background-color: var(--color-error);
|
||||
padding: var(--spacing-s);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.error__wrapper--no-overflow {
|
||||
@extend .error__wrapper;
|
||||
max-height: 10rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.error__text {
|
||||
color: var(--color-text-error);
|
||||
}
|
||||
|
||||
.help--error {
|
||||
@extend .help;
|
||||
color: var(--color-text-error);
|
||||
}
|
||||
|
||||
.thumbnail-preview {
|
||||
width: var(--thumbnail-preview-width);
|
||||
height: var(--thumbnail-preview-height);
|
||||
background-color: var(--color-thumbnail-background);
|
||||
padding: var(--spacing-s);
|
||||
font-size: var(--font-small);
|
||||
border-radius: var(--border-radius);
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.thumbnail-picker__preview {
|
||||
width: calc(var(--thumbnail-preview-width) * 1.5);
|
||||
height: calc(var(--thumbnail-preview-height) * 1.5);
|
||||
background-color: var(--color-thumbnail-background);
|
||||
padding: var(--spacing-s);
|
||||
font-size: var(--font-small);
|
||||
border-radius: var(--border-radius);
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.download-text {
|
||||
font-size: var(--font-xsmall);
|
||||
}
|
||||
|
||||
.notice-message {
|
||||
position: relative;
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacing-l);
|
||||
background-color: var(--color-primary-alt);
|
||||
|
||||
~ .card {
|
||||
margin-top: var(--spacing-m);
|
||||
}
|
||||
}
|
||||
|
||||
.notice-message--loud {
|
||||
@extend .notice-message;
|
||||
background-color: #fef1f6;
|
||||
color: var(--color-black);
|
||||
font-weight: bold;
|
||||
|
||||
.button {
|
||||
color: #fa6165;
|
||||
}
|
||||
}
|
||||
|
||||
.privacy-img {
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
.confirm__label {
|
||||
@extend label;
|
||||
}
|
||||
|
||||
.confirm__value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: var(--spacing-m);
|
||||
font-size: var(--font-large);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm__value--no-gap {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.confirm__value--subitem {
|
||||
font-size: var(--font-xsmall);
|
||||
}
|
||||
|
||||
.mobile-only {
|
||||
display: none;
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-hidden {
|
||||
@media (max-width: $breakpoint-small) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ads-test {
|
||||
height: 50vh;
|
||||
position: relative;
|
||||
|
||||
.video-js {
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.video-js .vjs-tech {
|
||||
height: 50vh;
|
||||
}
|
||||
}
|
||||
|
||||
.adspruce-bannerspot {
|
||||
height: 5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.release__notes {
|
||||
max-height: 50vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.signup__odysee-logo {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
margin-top: var(--spacing-xl);
|
||||
height: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.home__meme {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
font-size: 1rem;
|
||||
margin-bottom: var(--spacing-m);
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: var(--spacing-l);
|
||||
}
|
||||
}
|
237
web/scss/themes/odysee/init/_mixins.scss
Normal file
237
web/scss/themes/odysee/init/_mixins.scss
Normal file
|
@ -0,0 +1,237 @@
|
|||
@mixin between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@mixin breakpoint-max($breakpoint) {
|
||||
@media (max-width: #{$breakpoint}px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin breakpoint-min($breakpoint) {
|
||||
@media (min-width: #{$breakpoint}px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin center {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@mixin clearfix {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
|
||||
// (Smart) text truncation
|
||||
// Pass in a width to customize how much text is allowed
|
||||
// Omit value for basic text truncation
|
||||
@mixin constrict($value: null) {
|
||||
@if ($value) {
|
||||
max-width: $value;
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin create-grid($items-per-row: 4) {
|
||||
grid-template: repeat(1, 1fr) / repeat($items-per-row, 1fr);
|
||||
}
|
||||
|
||||
// Smart font include
|
||||
// Simply pass in the font-weight you want to use and the normal/italicized versions will be added
|
||||
// No more weighing down the front-end with references to unused weights
|
||||
@mixin font-face($font-weight, $relative-font-path, $font-name) {
|
||||
@font-face {
|
||||
font-family: $font-name;
|
||||
font-style: normal;
|
||||
font-weight: $font-weight;
|
||||
// sass-lint:disable indentation
|
||||
src: url('#{$relative-font-path}/#{$font-weight}.woff2') format('woff2'),
|
||||
url('#{$relative-font-path}/#{$font-weight}.woff') format('woff');
|
||||
// sass-lint:enable indentation
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: $font-name;
|
||||
font-style: italic;
|
||||
font-weight: $font-weight;
|
||||
// sass-lint:disable indentation
|
||||
src: url('#{$relative-font-path}/#{$font-weight}i.woff2') format('woff2'),
|
||||
url('#{$relative-font-path}/#{$font-weight}i.woff') format('woff');
|
||||
// sass-lint:enable indentation
|
||||
}
|
||||
}
|
||||
|
||||
@mixin font-mono {
|
||||
font-family: 'Fira Code', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
@mixin font-sans {
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
}
|
||||
|
||||
@mixin font-serif {
|
||||
font-family: Georgia, serif;
|
||||
}
|
||||
|
||||
@mixin hide-text {
|
||||
border: none;
|
||||
color: transparent;
|
||||
font: 0 / 0 a;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
// Cross-browser line-clamp support
|
||||
@mixin line-clamp($element-height: 2rem, $row-count: 2, $fade-color: var(--lbry-white), $computed-position: relative) {
|
||||
height: $element-height;
|
||||
overflow: hidden;
|
||||
position: $computed-position;
|
||||
|
||||
&::after {
|
||||
width: 50%;
|
||||
height: calc(#{$element-height} / #{$row-count});
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
background-image: linear-gradient(to right, rgba($lbry-white, 0), #{$fade-color} 80%);
|
||||
content: '';
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin no-user-select {
|
||||
user-select: none;
|
||||
|
||||
-ms-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
// Use CSS variables without upsetting Sass-Lint
|
||||
// https://github.com/sasstools/sass-lint/issues/1161#issuecomment-390537190
|
||||
@mixin root-prop($prop: null, $value: null) {
|
||||
@if ($prop and $value) {
|
||||
#{$prop}: $value;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin selection($background-color: var(--lbry-white), $text-color: var(--lbry-black)) {
|
||||
&::selection {
|
||||
background-color: $background-color;
|
||||
color: $text-color;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
&::-moz-selection {
|
||||
background-color: $background-color;
|
||||
color: $text-color;
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin thumbnail {
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
}
|
||||
|
||||
&::before {
|
||||
float: left;
|
||||
padding-top: var(--aspect-ratio-standard);
|
||||
}
|
||||
|
||||
&::after {
|
||||
clear: both;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin focus {
|
||||
box-shadow: 0 0 0 3px var(--color-focus);
|
||||
}
|
||||
|
||||
@mixin linkFocus {
|
||||
background-color: var(--color-link-focus-bg);
|
||||
box-shadow: 0 0 0 5px var(--color-link-focus-bg);
|
||||
}
|
||||
|
||||
@mixin underline($text-color: var(--lbry-black), $whitespace-color: var(--lbry-white)) {
|
||||
@include selection($text-color, $whitespace-color);
|
||||
|
||||
background-image: linear-gradient($whitespace-color, $whitespace-color),
|
||||
linear-gradient($whitespace-color, $whitespace-color), linear-gradient($text-color, $text-color);
|
||||
background-position: 0 88%, 100% 88%, 0 88%;
|
||||
background-repeat: no-repeat, no-repeat, repeat-x;
|
||||
background-size: 0.05rem 1px, 0.05rem 1px, 1px 1px;
|
||||
box-decoration-break: clone;
|
||||
display: inline;
|
||||
text-decoration: none;
|
||||
text-shadow: 0.03rem 0 $whitespace-color, -0.03rem 0 $whitespace-color, 0 0.03rem $whitespace-color,
|
||||
0 -0.03rem $whitespace-color, 0.06rem 0 $whitespace-color, -0.06rem 0 $whitespace-color, 0.09rem 0 $whitespace-color,
|
||||
-0.09rem 0 $whitespace-color, 0.12rem 0 $whitespace-color, -0.12rem 0 $whitespace-color, 0.15rem 0 $whitespace-color,
|
||||
-0.15rem 0 $whitespace-color;
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
// sass-lint:disable-line empty-args
|
||||
background-position: 0 90%, 100% 90%, 0 90%;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin placeholder {
|
||||
animation: pulse 2s infinite ease-in-out;
|
||||
background-color: var(--color-placeholder-background);
|
||||
border-radius: var(--card-radius);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
@mixin mediaThumbHoverZoom {
|
||||
.media__thumb,
|
||||
img {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.media__thumb,
|
||||
img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin handleClaimTileGifThumbnail($width) {
|
||||
.ff-canvas,
|
||||
.freezeframe-img {
|
||||
height: calc(#{$width} * (9 / 16)) !important;
|
||||
width: $width;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin handleClaimListGifThumbnail($width) {
|
||||
.ff-canvas,
|
||||
.freezeframe-img {
|
||||
height: calc(#{$width} * (9 / 16)) !important;
|
||||
width: $width;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin handleChannelGif($size) {
|
||||
height: $size;
|
||||
width: $size;
|
||||
|
||||
.ff-canvas,
|
||||
.freezeframe-img {
|
||||
border-radius: var(--border-radius);
|
||||
height: $size !important;
|
||||
width: $size !important;
|
||||
}
|
||||
}
|
245
web/scss/themes/odysee/init/_reset.scss
Normal file
245
web/scss/themes/odysee/init/_reset.scss
Normal file
|
@ -0,0 +1,245 @@
|
|||
html {
|
||||
box-sizing: border-box;
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
border: none;
|
||||
box-sizing: inherit;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
[disabled] {
|
||||
pointer-events: none;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
[readonly] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
[for],
|
||||
[role='button'],
|
||||
[type='button'],
|
||||
[type='checkbox'],
|
||||
[type='file'],
|
||||
[type='radio'],
|
||||
[type='select'],
|
||||
[type='submit'] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a,
|
||||
area,
|
||||
button,
|
||||
[role='button'],
|
||||
input,
|
||||
label,
|
||||
select,
|
||||
summary,
|
||||
textarea {
|
||||
// Remove touch delay on supported devices
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
list-style-position: inside;
|
||||
|
||||
> li {
|
||||
list-style-position: inside;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
dd {
|
||||
width: 80%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
dl {
|
||||
width: 100%;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
dt {
|
||||
width: 20%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: transparent;
|
||||
line-height: inherit;
|
||||
|
||||
&:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--color-gray-1);
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
|
||||
&::placeholder {
|
||||
color: inherit;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
&::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: var(--spacing-xxl);
|
||||
padding: var(--spacing-s);
|
||||
// border-color should be added in apps for blur/focus
|
||||
border: 1px solid;
|
||||
|
||||
&:not([disabled]) {
|
||||
resize: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
// sass-lint:disable-block no-important
|
||||
// Intelligent print styles
|
||||
pre,
|
||||
blockquote {
|
||||
border: 1px solid var(--color-gray-5) !important;
|
||||
page-break-inside: avoid !important;
|
||||
}
|
||||
|
||||
tr,
|
||||
img {
|
||||
page-break-inside: avoid !important;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
@page {
|
||||
margin: 0.5cm !important;
|
||||
}
|
||||
|
||||
p,
|
||||
h2,
|
||||
h3 {
|
||||
orphans: 3 !important;
|
||||
widows: 3 !important;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
page-break-after: avoid !important;
|
||||
}
|
||||
|
||||
thead {
|
||||
display: table-header-group !important;
|
||||
}
|
||||
|
||||
// Faster, more stable printing
|
||||
* {
|
||||
background-color: transparent !important;
|
||||
background-image: none !important;
|
||||
color: var(--lbry-black) !important;
|
||||
filter: none !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
p {
|
||||
a {
|
||||
&[href]::after {
|
||||
// Show hypertext data for links and abbreviations
|
||||
content: ' (' attr(href) ')' !important;
|
||||
}
|
||||
|
||||
&[href^='javascript:'],
|
||||
&[href^='#'] {
|
||||
&::after {
|
||||
content: '' !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abbr {
|
||||
&[title]::after {
|
||||
content: ' (' attr(title) ')' !important;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
abbr {
|
||||
text-decoration: underline !important;
|
||||
word-wrap: break-word !important;
|
||||
}
|
||||
}
|
||||
}
|
108
web/scss/themes/odysee/init/_vars.scss
Normal file
108
web/scss/themes/odysee/init/_vars.scss
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Both of these should probably die and become variables as well
|
||||
$spacing-vertical: 2rem;
|
||||
$spacing-width: 36px;
|
||||
|
||||
$breakpoint-xxsmall: 450px;
|
||||
$breakpoint-xsmall: 600px;
|
||||
$breakpoint-small: 900px;
|
||||
$breakpoint-medium: 1150px;
|
||||
$breakpoint-large: 1600px;
|
||||
|
||||
:root {
|
||||
--border-radius: 10px;
|
||||
--height-input: 2.5rem;
|
||||
--height-button: 2.5rem;
|
||||
--height-checkbox: 24px;
|
||||
--height-radio: 24px;
|
||||
--height-badge: 24px;
|
||||
|
||||
// Spacing
|
||||
--spacing-xxs: calc(2rem / 5);
|
||||
--spacing-xs: calc(2rem / 4);
|
||||
--spacing-s: calc(2rem / 3);
|
||||
--spacing-m: calc(2rem / 2);
|
||||
--spacing-l: 2rem;
|
||||
--spacing-xl: 3rem;
|
||||
|
||||
// Aspect ratio
|
||||
--aspect-ratio-bluray: 41.6666666667%; // 12:5
|
||||
--aspect-ratio-panavision: 36.3636363636%; // 11:4
|
||||
--aspect-ratio-sd: 75%; // 4:3
|
||||
--aspect-ratio-standard: 56.25%; // 16:9
|
||||
|
||||
// Type
|
||||
--font-mono: 'Fira Code';
|
||||
--font-sans: Inter;
|
||||
--font-serif: Georgia;
|
||||
--font-weight-base: 400;
|
||||
--font-weight-light: 300;
|
||||
--font-weight-bold: 700;
|
||||
--font-base: 14px;
|
||||
--font-body: 1rem;
|
||||
--font-xxsmall: 0.65rem;
|
||||
--font-xsmall: 0.7344rem;
|
||||
--font-small: 0.8571rem;
|
||||
--font-large: 1.3rem;
|
||||
--font-title: 1.71rem;
|
||||
--font-heading: 2.94rem;
|
||||
|
||||
// Width & spacing
|
||||
--page-max-width: 1280px;
|
||||
--page-max-width--filepage: 1700px;
|
||||
--mac-titlebar-height: 24px;
|
||||
--mobile: 600px;
|
||||
--side-nav-width: 230px;
|
||||
--side-nav-width--micro: 125px;
|
||||
|
||||
--spacing-main-padding: var(--spacing-xl);
|
||||
--floating-viewer-width: 32rem;
|
||||
--floating-viewer-height: 18rem; // 32 * 9/16
|
||||
--floating-viewer-info-height: 5rem;
|
||||
--floating-viewer-container-height: calc(var(--floating-viewer-height) + var(--floating-viewer-info-height));
|
||||
--option-select-width: 8rem;
|
||||
|
||||
// Text
|
||||
--text-max-width: 660px;
|
||||
--text-link-padding: 4px;
|
||||
|
||||
// Tabs
|
||||
--tab-indicator-size: 0.5rem;
|
||||
|
||||
// Header
|
||||
// This is tied to the floating player so it knows where to attach to
|
||||
// ui/component/fileRenderFloating/view.jsx
|
||||
--header-height: 80px;
|
||||
|
||||
// Inline Player
|
||||
--inline-player-max-height: calc(100vh - var(--header-height) - var(--spacing-l) * 2);
|
||||
|
||||
// Card
|
||||
--card-radius: var(--border-radius);
|
||||
--card-max-width: 1000px;
|
||||
--card-box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
|
||||
// Modal
|
||||
--modal-width: 550px;
|
||||
|
||||
// Animation :)
|
||||
--animation-duration: 0.2s;
|
||||
--animation-style: ease-in-out;
|
||||
|
||||
// Image
|
||||
--thumbnail-preview-height: 100px;
|
||||
--thumbnail-preview-width: 177px;
|
||||
--cover-photo-height: 210px;
|
||||
--channel-thumbnail-width: 10rem;
|
||||
--channel-thumbnail-width--small: 4rem;
|
||||
--file-list-thumbnail-width: 10rem;
|
||||
|
||||
--tag-height: 1.5rem;
|
||||
|
||||
--livestream-comments-width: 30rem;
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
:root {
|
||||
--font-body: 0.8rem;
|
||||
}
|
||||
}
|
155
web/scss/themes/odysee/light.scss
Normal file
155
web/scss/themes/odysee/light.scss
Normal file
|
@ -0,0 +1,155 @@
|
|||
:root {
|
||||
// Color overrides
|
||||
--color-primary: #fa6165;
|
||||
--color-primary-alt: #fef1f6;
|
||||
--color-primary-alt-2: #fb7e82;
|
||||
--color-primary-alt-3: #fbcbdd;
|
||||
--color-secondary: #f9902a;
|
||||
--color-secondary-alt: #fee8d2;
|
||||
--color-secondary-alt-2: #fefcf6;
|
||||
|
||||
// Structure
|
||||
--color-border: #ededed;
|
||||
--color-background: #fafafa;
|
||||
--color-background-overlay: #21252980;
|
||||
--color-card-background: #ffffff;
|
||||
--color-card-background-highlighted: #fff5f5;
|
||||
|
||||
// Text
|
||||
--color-text-selection-bg: var(--color-primary-alt);
|
||||
--color-text-selection: var(--color-primary);
|
||||
--color-text-error: var(--color-danger);
|
||||
--color-text-empty: #999999;
|
||||
--color-text-help: #999999;
|
||||
--color-text-subtitle: #767676;
|
||||
--color-text-warning: #212529;
|
||||
--color-help-warning-bg: #fef3c7;
|
||||
--color-text-warning--background: var(--lbry-yellow-1);
|
||||
--color-blockquote: var(--color-gray-3);
|
||||
--color-blockquote-bg: var(--color-gray-1);
|
||||
--color-tooltip-bg: #222;
|
||||
--color-tooltip-text: #fafafa;
|
||||
|
||||
// Header
|
||||
--color-header-button: var(--color-button-alt-bg);
|
||||
--color-header-background: #ffffff;
|
||||
|
||||
// Button
|
||||
--color-button-alt-bg: var(--color-gray-1);
|
||||
--color-button-alt-bg-hover: var(--color-gray-2);
|
||||
--color-button-alt-text: black;
|
||||
--color-button-primary-bg: var(--color-primary);
|
||||
--color-button-primary-bg-hover: var(--color-primary-alt-2);
|
||||
--color-button-primary-text: var(--color-primary-alt);
|
||||
--color-button-primary-hover-text: var(--color-white);
|
||||
--color-button-secondary-bg: var(--color-primary-alt);
|
||||
--color-button-secondary-border: var(--color-primary-alt-3);
|
||||
--color-button-secondary-text: var(--color-primary);
|
||||
--color-button-secondary-bg-hover: var(--color-primary-alt-3);
|
||||
--color-button-toggle-text: var(--color-primary);
|
||||
--color-button-toggle-bg: var(--color-primary-alt);
|
||||
--color-button-toggle-bg-hover: var(--color-primary-alt);
|
||||
--color-button-border: var(--color-gray-3);
|
||||
--color-link-active: var(--color-primary);
|
||||
--color-link-focus-bg: #f1f1f1;
|
||||
--color-link: var(--color-primary);
|
||||
|
||||
// Input
|
||||
--color-input-bg-selected: var(--color-primary-alt);
|
||||
--color-input-color: #111111;
|
||||
--color-input-label: var(--color-gray-5);
|
||||
--color-input-placeholder: #212529;
|
||||
--color-input-bg: var(--color-gray-1);
|
||||
--color-input-border: var(--color-border);
|
||||
--color-input-border-active: var(--color-secondary);
|
||||
--color-input-toggle: var(--color-secondary);
|
||||
--color-input-toggle-bg: var(--color-gray-1);
|
||||
--color-input-toggle-bg-hover: var(--color-secondary-alt);
|
||||
--color-input-prefix-bg: var(--color-gray-2);
|
||||
--color-input-prefix-border: var(--color-gray-5);
|
||||
--select-toggle-background: url("data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg' fill='%23212529'%3E%3Cpath d='M17.172, 31.172c1.562, -1.562 4.095, -1.562 5.656, 0l25.172, 25.171l25.172, -25.171c1.562, -1.562 4.095, -1.562 5.656, 0c1.562, 1.562 1.562, 4.095 0, 5.656l-28, 28c-1.562, 1.562 -4.095, 1.562 -5.656, 0l-28, -28c-0.781, -0.781 -1.172, -1.805 -1.172, -2.828c0, -1.023 0.391, -2.047 1.172, -2.828Z'/%3E%3C/svg%3E%0A");
|
||||
|
||||
// Navigation
|
||||
--color-navigation-icon: var(--color-gray-5);
|
||||
--color-navigation-link: var(--color-gray-5);
|
||||
--color-navigation-active: var(--color-primary-alt);
|
||||
--color-navigation-active-text: var(--color-primary);
|
||||
--color-navigation-hover: var(--color-gray-1);
|
||||
--color-navigation-hover-text: #3f3f3f;
|
||||
|
||||
// Tags
|
||||
--color-tag: var(--color-primary-alt-2);
|
||||
--color-tag-bg: #f9f6f7;
|
||||
--color-tag-hover: var(--color-button-alt-text);
|
||||
--color-tag-bg-hover: var(--color-button-alt-bg-hover);
|
||||
|
||||
// Menu
|
||||
--color-menu-background: var(--color-header-background);
|
||||
--color-menu-icon: var(--color-navigation-link);
|
||||
--color-menu-icon-active: var(--color-navigation-link);
|
||||
--color-menu-background--selected: var(--color-secondary-alt);
|
||||
--color-menu-background--active: var(--color-primary-alt);
|
||||
|
||||
// Comments
|
||||
--color-comment-menu: #e0e0e0;
|
||||
--color-comment-menu-hovering: #6a6a6a;
|
||||
--color-comment-highlighted: #fff2d9;
|
||||
--color-comment-threadline: var(--color-gray-1);
|
||||
--color-comment-threadline-hover: var(--color-gray-4);
|
||||
|
||||
// Superchat
|
||||
--color-superchat-text: var(--color-black);
|
||||
--color-superchat: #fcd34d;
|
||||
--color-superchat__light: #fcd34d50;
|
||||
--color-superchat-2: #fde68a;
|
||||
--color-superchat-3: #fef3c7;
|
||||
--color-superchat-3__light: #fef3c750;
|
||||
--color-superchat-4: #fffbeb;
|
||||
|
||||
// Color
|
||||
--color-focus: #8dbff0;
|
||||
--color-nag: #fa8700;
|
||||
--color-error: #fcafca;
|
||||
--color-notice: #fef3ca;
|
||||
--color-purchased: var(--color-cost);
|
||||
--color-purchased-alt: #ffebc2;
|
||||
--color-purchased-text: black;
|
||||
--color-thumbnail-background: var(--color-gray-1);
|
||||
--color-spinner-light: #ffffff;
|
||||
--color-spinner-dark: #212529;
|
||||
--color-placeholder-background: #f0f0f0;
|
||||
--color-file-viewer-background: var(--color-card-background);
|
||||
--color-tabs-background: var(--color-card-background);
|
||||
--color-tab-divider: var(--color-primary);
|
||||
--color-modal-background: var(--color-card-background);
|
||||
|
||||
// Icons
|
||||
--color-follow-bg: #ffd4da;
|
||||
--color-follow-icon: #e2495e;
|
||||
--color-view-bg: var(--color-secondary-alt);
|
||||
--color-view-icon: var(--color-secondary);
|
||||
|
||||
// Editor
|
||||
--color-editor-cursor: var(--color-text);
|
||||
--color-editor-quote: #707070;
|
||||
--color-editor-tag: #ea9400;
|
||||
--color-editor-attr: #04b0f4;
|
||||
--color-editor-string: #ff7451;
|
||||
--color-editor-inline-code-fg: var(--color-text);
|
||||
--color-editor-inline-code-fg-preview: #2e3439;
|
||||
--color-editor-inline-code-bg: rgba(157, 161, 165, 0.3);
|
||||
--color-editor-inline-code-bg-preview: #d0e8ff;
|
||||
--color-editor-selected: #add6ff;
|
||||
--color-editor-link: var(--color-link);
|
||||
--color-editor-url: var(--color-editor-string);
|
||||
--color-editor-hr: var(--color-editor-tag);
|
||||
--color-editor-hr-preview: #cccccc;
|
||||
|
||||
// Ads
|
||||
--color-ads-background: #fae5ff;
|
||||
--color-ads-link: var(--color-link);
|
||||
|
||||
// Scrollbar
|
||||
--color-scrollbar-thumb-bg: rgba(0, 0, 0, 0.2);
|
||||
--color-scrollbar-track-bg: transparent;
|
||||
}
|
|
@ -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');
|
||||
|
@ -82,20 +83,31 @@ function buildOgMetadata(overrideOptions = {}) {
|
|||
function conditionallyAddPWA() {
|
||||
let head = '';
|
||||
if (DOMAIN === 'odysee.com') {
|
||||
head += '<link rel="manifest" href="./public/pwa/manifest.json"/>';
|
||||
head += '<link rel="manifest" href="./public/pwa/manifest.json"/>';
|
||||
head += '<link rel="apple-touch-icon" sizes="180x180" href="./public/pwa/icon-180.png">';
|
||||
head += '<script src="./serviceWorker.js"></script>';
|
||||
}
|
||||
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>`
|
||||
);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue