diff --git a/.eslintrc.json b/.eslintrc.json index 83b2a0606..d3bcd1f7b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -36,6 +36,7 @@ "func-names": ["warn", "as-needed"], "jsx-a11y/label-has-for": 0, "import/prefer-default-export": 0, - "no-return-assign": 0 + "no-return-assign": 0, + "react/require-default-props": 0 } } diff --git a/.travis.yml b/.travis.yml index 473dc35d6..7997139a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,19 @@ matrix: include: - os: osx + env: TARGET=mac osx_image: xcode9.2 language: node_js node_js: "9" - env: - - ELECTRON_CACHE=$HOME/.cache/electron - - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder - os: linux + env: TARGET=windows services: docker language: node_js node_js: "9" + - os: linux + env: TARGET=linux + language: node_js + node_js: "9" cache: false before_install: - | @@ -20,6 +23,7 @@ before_install: else sudo apt-get -qq update sudo apt-get install -y libsecret-1-dev + sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.6.0 export PATH="$HOME/.yarn/bin:$PATH" fi @@ -27,17 +31,15 @@ before_script: - git lfs pull script: - | - if [ "$TRAVIS_OS_NAME" == "linux" ]; then + if [ "$TARGET" == "windows" ]; then docker run --rm \ --env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|GH_|GITHUB_|BT_|AWS_|STRIP|BUILD_') \ -v ${PWD}:/project \ - -v ~/.cache/electron:/root/.cache/electron \ - -v ~/.cache/electron-builder:/root/.cache/electron-builder \ electronuserland/builder:wine \ - /bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn build --linux --win --publish onTag" - else - yarn build --publish onTag + /bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn build --win --publish onTag"; fi + - if [ "$TARGET" == "mac" ]; then yarn build --publish onTag; fi + - if [ "$TARGET" == "linux" ]; then yarn --link-duplicates --pure-lockfile && yarn build --linux --publish onTag; fi branches: except: - "/^v\\d+\\.\\d+\\.\\d+$/" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f0e50e0f..f2e2737bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,11 +14,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). * Pre-fill publish URL after clicking "Put something here" link ([#1303](https://github.com/lbryio/lbry-app/pull/1303)) * Add Danger JS to automate code reviews ([#1289](https://github.com/lbryio/lbry-app/pull/1289)) * Autoplay downloaded and free media ([#584](https://github.com/lbryio/lbry-app/pull/1453)) + * Add 'Go to page' input on channel pagination ([#1166](https://github.com/lbryio/lbry-app/pull/1166)) + * Add "View on web" button on file/channel pages with spee.ch link ([#1222](https://github.com/lbryio/lbry-app/pull/1222)) ### Changed * Add flair to snackbar ([#1313](https://github.com/lbryio/lbry-app/pull/1313)) * Made font in price badge larger ([#1420](https://github.com/lbryio/lbry-app/pull/1420)) * Store subscriptions in internal database ([#1424](https://github.com/lbryio/lbry-app/pull/1424)) + * Move rewards logic to interal api ([#1509](https://github.com/lbryio/lbry-app/pull/1509)) ### Fixed * Fix content-type not shown correctly in file description ([#863](https://github.com/lbryio/lbry-app/pull/863)) @@ -57,7 +60,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). * App category for Linux ([#877](https://github.com/lbryio/lbry-app/pull/877)) * Add YouTube Sync reward ([#1147](https://github.com/lbryio/lbry-app/pull/1147)) * Retain previous screen sizing on startup ([#338](https://github.com/lbryio/lbry-app/issues/338)) - * Add 'Go to page' input on channel pagination ([#1166](https://github.com/lbryio/lbry-app/pull/1166)) ### Changed @@ -372,7 +374,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). * Removed placeholder values from price selection form fields, which was causing confusion that these were real values (#426) * Fixed showing "other currency" help tip in publish form, which was caused due to not "setting" state for price * Publish page now properly checks for all required fields are filled - * Fixed pagination styling for pages > 5 (#416) * Fixed sizing on squat videos (#419) * Support claims no longer show up on Published page (#384) * Fixed rendering of small prices (#461) @@ -410,7 +411,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added * Replaced horizontal scrollbars with scroll arrows * Featured weekly reward content shows with an orange star - * Added pagination to channel pages ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6c05963c..07edc7648 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -146,6 +146,8 @@ There are a few tools integrated to the project that will ease the process of de manner and, therefore, not begin working on anything reserved (or updated) within the last 3 days. If someone has been officially assigned an issue via Github's assignment system, it is also not available. Contributors are encouraged to ask if they have any questions about issue availability. +* The [changelog](https://github.com/lbryio/lbry-app/blob/master/CHANGELOG.md) should be updated to + include a referene to the fix/change/addition. See previous entries for format. * Once the pull request is visible in the LBRY repo, a LBRY team member will review it and make sure it is up to our standards. At this point, the contributor may have to change his or her code based on our suggestions and comments. diff --git a/README.md b/README.md index 2e745e758..8826ff10b 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ distributable packages. #### Resetting your Packages -If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del node_modules && yarn` on Windows. +If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del /s /q node_modules && yarn` on Windows. ## Contributing diff --git a/package.json b/package.json index d279738d6..bcf7cf160 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "formik": "^0.10.4", "hast-util-sanitize": "^1.1.2", "keytar": "^4.2.1", - "lbry-redux": "lbryio/lbry-redux#30c18725d8c6c141c30c57f0a324d0abb8963b99", + "lbry-redux": "lbryio/lbry-redux#fb27fb66387043be1b7962cd9439845fa73d7d8e", "localforage": "^1.7.1", "mixpanel-browser": "^2.17.1", "moment": "^2.22.0", diff --git a/src/main/createWindow.js b/src/main/createWindow.js index 738ab792a..764003c65 100644 --- a/src/main/createWindow.js +++ b/src/main/createWindow.js @@ -3,7 +3,6 @@ import isDev from 'electron-is-dev'; import windowStateKeeper from 'electron-window-state'; import setupBarMenu from './menu/setupBarMenu'; -import setupContextMenu from './menu/setupContextMenu'; export default appState => { // Get primary display dimensions from Electron. @@ -73,7 +72,6 @@ export default appState => { } setupBarMenu(); - setupContextMenu(window); window.on('close', event => { if (!appState.isQuitting && !appState.autoUpdateAccepted) { diff --git a/src/main/index.js b/src/main/index.js index f5e46ae91..c678f6579 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -179,11 +179,11 @@ ipcMain.on('version-info-requested', () => { }, }; let result = ''; - - const req = https.get(Object.assign(opts, url.parse(latestReleaseAPIURL)), res => { + const onSuccess = res => { res.on('data', data => { result += data; }); + res.on('end', () => { const tagName = JSON.parse(result).tag_name; const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/); @@ -202,14 +202,27 @@ ipcMain.on('version-info-requested', () => { } } }); - }); + }; - req.on('error', err => { - console.log('Failed to get current version from GitHub. Error:', err); - if (rendererWindow) { - rendererWindow.webContents.send('version-info-received', null); - } - }); + const requestLatestRelease = (apiUrl, alreadyRedirected = false) => { + const req = https.get(Object.assign(opts, url.parse(apiUrl)), res => { + if (res.statusCode === 301 || res.statusCode === 302) { + requestLatestRelease(res.headers.location, true); + } else { + onSuccess(res); + } + }); + + if (alreadyRedirected) return; + req.on('error', err => { + console.log('Failed to get current version from GitHub. Error:', err); + if (rendererWindow) { + rendererWindow.webContents.send('version-info-received', null); + } + }); + }; + + requestLatestRelease(latestReleaseAPIURL); }); ipcMain.on('get-auth-token', event => { @@ -245,8 +258,11 @@ const isSecondInstance = app.makeSingleInstance(argv => { // path, so we just strip it off. // - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as // an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well. + // - ? also interpreted as an anchor, remove slash also. if (process.platform === 'win32') { - URI = URI.replace(/\/$/, '').replace('/#', '#'); + URI = URI.replace(/\/$/, '') + .replace('/#', '#') + .replace('/?', '?'); } rendererWindow.webContents.send('open-uri-requested', URI); diff --git a/src/main/menu/setupContextMenu.js b/src/main/menu/setupContextMenu.js deleted file mode 100644 index 658ac731a..000000000 --- a/src/main/menu/setupContextMenu.js +++ /dev/null @@ -1,27 +0,0 @@ -// @flow -import { Menu, BrowserWindow } from 'electron'; -import isDev from 'electron-is-dev'; - -export default (rendererWindow: BrowserWindow) => { - rendererWindow.webContents.on('context-menu', (e, params) => { - const { x, y } = params; - - const template = [{ role: 'cut' }, { role: 'copy' }, { role: 'paste' }]; - - const developmentTemplateAddition = [ - { type: 'separator' }, - { - label: 'Inspect element', - click: () => { - rendererWindow.inspectElement(x, y); - }, - }, - ]; - - if (isDev) { - template.push(...developmentTemplateAddition); - } - - Menu.buildFromTemplate(template).popup(); - }); -}; diff --git a/src/renderer/component/button/view.jsx b/src/renderer/component/button/view.jsx index f622ff28a..8b496ea12 100644 --- a/src/renderer/component/button/view.jsx +++ b/src/renderer/component/button/view.jsx @@ -22,6 +22,7 @@ type Props = { button: ?string, // primary, secondary, alt, link noPadding: ?boolean, // to remove padding and allow circular buttons uppercase: ?boolean, + iconColor: ?string, }; class Button extends React.PureComponent { @@ -48,6 +49,7 @@ class Button extends React.PureComponent { type, noPadding, uppercase, + iconColor, ...otherProps } = this.props; @@ -82,10 +84,10 @@ class Button extends React.PureComponent { const content = ( - {icon && } + {icon && } {label && {label}} {children && children} - {iconRight && } + {iconRight && } ); diff --git a/src/renderer/component/categoryList/index.js b/src/renderer/component/categoryList/index.js new file mode 100644 index 000000000..7e728b9f4 --- /dev/null +++ b/src/renderer/component/categoryList/index.js @@ -0,0 +1,18 @@ +import { connect } from 'react-redux'; +import { doFetchClaimsByChannel } from 'redux/actions/content'; +import { + makeSelectClaimsInChannelForCurrentPage, + makeSelectFetchingChannelClaims, +} from 'lbry-redux'; +import CategoryList from './view'; + +const select = (state, props) => ({ + channelClaims: makeSelectClaimsInChannelForCurrentPage(props.categoryLink)(state), + fetching: makeSelectFetchingChannelClaims(props.categoryLink)(state), +}); + +const perform = dispatch => ({ + fetchChannel: channel => dispatch(doFetchClaimsByChannel(channel)), +}); + +export default connect(select, perform)(CategoryList); diff --git a/src/renderer/component/common/category-list.jsx b/src/renderer/component/categoryList/view.jsx similarity index 91% rename from src/renderer/component/common/category-list.jsx rename to src/renderer/component/categoryList/view.jsx index 4b1589cee..0afa57940 100644 --- a/src/renderer/component/common/category-list.jsx +++ b/src/renderer/component/categoryList/view.jsx @@ -5,11 +5,15 @@ import ToolTip from 'component/common/tooltip'; import FileCard from 'component/fileCard'; import Button from 'component/button'; import * as icons from 'constants/icons'; +import Claim from 'types/claim'; type Props = { category: string, names: Array, - categoryLink?: string, + categoryLink: ?string, + fetching: boolean, + channelClaims: Array, + fetchChannel: string => void, }; type State = { @@ -18,6 +22,11 @@ type State = { }; class CategoryList extends React.PureComponent { + static defaultProps = { + names: [], + categoryLink: '', + }; + constructor() { super(); @@ -32,6 +41,11 @@ class CategoryList extends React.PureComponent { } componentDidMount() { + const { fetching, categoryLink, fetchChannel } = this.props; + if (!fetching) { + fetchChannel(categoryLink); + } + const cardRow = this.rowItems; if (cardRow) { const cards = cardRow.getElementsByTagName('section'); @@ -109,6 +123,9 @@ class CategoryList extends React.PureComponent { // check if a card is fully visible horizontally isCardVisible = (section: HTMLElement) => { + if (!section) { + return false; + } const rect = section.getBoundingClientRect(); const isVisible = rect.left >= 0 && rect.right <= window.innerWidth; return isVisible; @@ -189,7 +206,7 @@ class CategoryList extends React.PureComponent { } render() { - const { category, names, categoryLink } = this.props; + const { category, categoryLink, names, channelClaims } = this.props; const { canScrollNext, canScrollPrevious } = this.state; // The lint was throwing an error saying we should use -
{body}
+ + {tooltipContent} + {body} ); } diff --git a/src/renderer/component/fileActions/view.jsx b/src/renderer/component/fileActions/view.jsx index 94995d245..4ab110fca 100644 --- a/src/renderer/component/fileActions/view.jsx +++ b/src/renderer/component/fileActions/view.jsx @@ -1,7 +1,6 @@ // @flow import React from 'react'; import Button from 'component/button'; -import FileDownloadLink from 'component/fileDownloadLink'; import { MODALS } from 'lbry-redux'; import classnames from 'classnames'; import * as icons from 'constants/icons'; @@ -27,21 +26,21 @@ class FileActions extends React.PureComponent { return (
- {showDelete && (
diff --git a/src/renderer/component/fileCard/view.jsx b/src/renderer/component/fileCard/view.jsx index 88bb99946..3655c7c61 100644 --- a/src/renderer/component/fileCard/view.jsx +++ b/src/renderer/component/fileCard/view.jsx @@ -1,6 +1,6 @@ // @flow import * as React from 'react'; -import { normalizeURI } from 'lbry-redux'; +import { normalizeURI, convertToShareLink } from 'lbry-redux'; import Button from 'component/button'; import CardMedia from 'component/cardMedia'; import TruncatedText from 'component/common/truncated-text'; @@ -9,6 +9,7 @@ import FilePrice from 'component/filePrice'; import UriIndicator from 'component/uriIndicator'; import * as icons from 'constants/icons'; import classnames from 'classnames'; +import { openCopyLinkMenu } from '../../util/contextMenu'; // TODO: iron these out type Props = { @@ -60,6 +61,9 @@ class FileCard extends React.PureComponent { const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null; const shouldObscureNsfw = obscureNsfw && metadata && metadata.nsfw; const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id); + const handleContextMenu = event => { + openCopyLinkMenu(convertToShareLink(uri), event); + }; // We should be able to tab through cards /* eslint-disable jsx-a11y/click-events-have-key-events */ @@ -72,6 +76,7 @@ class FileCard extends React.PureComponent { 'card--link': !pending, 'card--pending': pending, })} + onContextMenu={handleContextMenu} >
{showPrice && }
@@ -98,14 +103,16 @@ class FileCard extends React.PureComponent {
{title}
-
+
{pending ? (
Pending...
) : ( - {isRewardContent && } - {fileInfo && } +
+ {isRewardContent && } + {fileInfo && } +
)}
diff --git a/src/renderer/component/fileDownloadLink/index.js b/src/renderer/component/fileDownloadLink/index.js index d36f4c896..22f4f0a3f 100644 --- a/src/renderer/component/fileDownloadLink/index.js +++ b/src/renderer/component/fileDownloadLink/index.js @@ -5,7 +5,6 @@ import { makeSelectLoadingForUri, makeSelectCostInfoForUri, } from 'lbry-redux'; -import { doFetchAvailability } from 'redux/actions/availability'; import { doOpenFileInShell } from 'redux/actions/file'; import { doPurchaseUri, doStartDownload } from 'redux/actions/content'; import { doPause } from 'redux/actions/media'; @@ -20,7 +19,6 @@ const select = (state, props) => ({ }); const perform = dispatch => ({ - checkAvailability: uri => dispatch(doFetchAvailability(uri)), openInShell: path => dispatch(doOpenFileInShell(path)), purchaseUri: uri => dispatch(doPurchaseUri(uri)), restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)), diff --git a/src/renderer/component/fileDownloadLink/view.jsx b/src/renderer/component/fileDownloadLink/view.jsx index c2c413bd6..8d96624f8 100644 --- a/src/renderer/component/fileDownloadLink/view.jsx +++ b/src/renderer/component/fileDownloadLink/view.jsx @@ -1,21 +1,29 @@ +// @flow import React from 'react'; import Button from 'component/button'; -import classnames from 'classnames'; import * as icons from 'constants/icons'; -class FileDownloadLink extends React.PureComponent { - componentWillMount() { - this.checkAvailability(this.props.uri); - } - - componentWillReceiveProps(nextProps) { - this.checkAvailability(nextProps.uri); - this.restartDownload(nextProps); - } - - restartDownload(props) { - const { downloading, fileInfo, uri, restartDownload } = props; +type Props = { + uri: string, + downloading: boolean, + fileInfo: ?{ + written_bytes: number, + total_bytes: number, + outpoint: number, + download_path: string, + completed: boolean, + }, + loading: boolean, + costInfo: ?{}, + restartDownload: (string, number) => void, + openInShell: string => void, + purchaseUri: string => void, + doPause: () => void, +}; +class FileDownloadLink extends React.PureComponent { + componentWillUpdate() { + const { downloading, fileInfo, uri, restartDownload } = this.props; if ( !downloading && fileInfo && @@ -27,12 +35,7 @@ class FileDownloadLink extends React.PureComponent { } } - checkAvailability(uri) { - if (!this._uri || uri !== this._uri) { - this._uri = uri; - this.props.checkAvailability(uri); - } - } + uri: ?string; render() { const { @@ -47,8 +50,10 @@ class FileDownloadLink extends React.PureComponent { } = this.props; const openFile = () => { - openInShell(fileInfo.download_path); - doPause(); + if (fileInfo) { + openInShell(fileInfo.download_path); + doPause(); + } }; if (loading || downloading) { @@ -56,21 +61,11 @@ class FileDownloadLink extends React.PureComponent { fileInfo && fileInfo.written_bytes ? fileInfo.written_bytes / fileInfo.total_bytes * 100 : 0; - const label = fileInfo ? progress.toFixed(0) + __('% complete') : __('Connecting...'); + const label = fileInfo + ? __('Downloading: ') + progress.toFixed(0) + __('% complete') + : __('Connecting...'); - return ( -
-
- {label} -
- {label} -
- ); + return {label}; } else if (fileInfo === null && !downloading) { if (!costInfo) { return null; @@ -78,9 +73,10 @@ class FileDownloadLink extends React.PureComponent { return (
{contentList}
diff --git a/src/renderer/page/discover/view.jsx b/src/renderer/page/discover/view.jsx index 28adf330c..123992dc8 100644 --- a/src/renderer/page/discover/view.jsx +++ b/src/renderer/page/discover/view.jsx @@ -1,7 +1,7 @@ // @flow import React from 'react'; import Page from 'component/page'; -import CategoryList from 'component/common/category-list'; +import CategoryList from 'component/categoryList'; type Props = { fetchFeaturedUris: () => void, @@ -27,7 +27,11 @@ class DiscoverPage extends React.PureComponent { featuredUris[category].length ? ( ) : ( - '' + ) )} {failedToLoad &&
{__('Failed to load landing content.')}
} diff --git a/src/renderer/page/file/view.jsx b/src/renderer/page/file/view.jsx index bf7c34846..e8392ff6c 100644 --- a/src/renderer/page/file/view.jsx +++ b/src/renderer/page/file/view.jsx @@ -13,10 +13,13 @@ import DateTime from 'component/dateTime'; import * as icons from 'constants/icons'; import Button from 'component/button'; import SubscribeButton from 'component/subscribeButton'; +import ViewOnWebButton from 'component/viewOnWebButton'; import Page from 'component/page'; import player from 'render-media'; import * as settings from 'constants/settings'; import type { Claim } from 'types/claim'; +import type { Subscription } from 'types/subscription'; +import FileDownloadLink from 'component/fileDownloadLink'; type Props = { claim: Claim, @@ -39,10 +42,10 @@ type Props = { openModal: ({ id: string }, { uri: string }) => void, fetchFileInfo: string => void, fetchCostInfo: string => void, - prepareEdit: ({}) => void, + prepareEdit: ({}, string) => void, setClientSetting: (string, boolean | string) => void, checkSubscription: ({ channelName: string, uri: string }) => void, - subscriptions: Array<{}>, + subscriptions: Array, }; class FilePage extends React.Component { @@ -78,12 +81,7 @@ class FilePage extends React.Component { } checkSubscription = (props: Props) => { - if ( - props.claim.value.publisherSignature && - props.subscriptions - .map(subscription => subscription.channelName) - .indexOf(props.claim.channel_name) !== -1 - ) { + if (props.subscriptions.find(sub => sub.channelName === props.claim.channel_name)) { props.checkSubscription({ channelName: props.claim.channel_name, uri: buildURI( @@ -112,6 +110,7 @@ class FilePage extends React.Component { prepareEdit, navigate, autoplay, + costInfo, } = this.props; // File info @@ -128,6 +127,19 @@ class FilePage extends React.Component { if (channelName && channelClaimId) { subscriptionUri = buildURI({ channelName, claimId: channelClaimId }, false); } + const speechSharable = + costInfo && + costInfo.cost === 0 && + contentType && + ['video', 'image'].includes(contentType.split('/')[0]); + + // We want to use the short form uri for editing + // This is what the user is used to seeing, they don't care about the claim id + // We will select the claim id before they publish + let editUri; + if (claimIsMine) { + editUri = buildURI({ channelName, contentName: claim.name }); + } const isPlaying = playingUri === uri && !isPaused; return ( @@ -143,17 +155,12 @@ class FilePage extends React.Component { ) : ( )} - {!isPlaying && ( -
- -
- )}

{title}

- {isRewardContent && } + {isRewardContent && }
@@ -163,6 +170,7 @@ class FilePage extends React.Component { {metadata.nsfw &&
NSFW
}
+
{claimIsMine ? (
@@ -199,6 +215,11 @@ class FilePage extends React.Component {
+ + +
+ +
diff --git a/src/renderer/page/publish/index.js b/src/renderer/page/publish/index.js index 864ab0df5..78950abe7 100644 --- a/src/renderer/page/publish/index.js +++ b/src/renderer/page/publish/index.js @@ -35,6 +35,7 @@ const select = (state, props) => { const claimsByUri = selectClaimsByUri(state); const myClaims = selectMyClaims(state); + const myChannels = selectMyChannelClaims(state); const claimForUri = claimsByUri[uri]; let winningBidForClaimUri; @@ -50,6 +51,7 @@ const select = (state, props) => { claimForUri, winningBidForClaimUri, myClaimForUri, + myChannels, costInfo: makeSelectCostInfoForUri(props.uri)(state), balance: selectBalance(state), }; diff --git a/src/renderer/page/search/view.jsx b/src/renderer/page/search/view.jsx index 0535c34d7..763848def 100644 --- a/src/renderer/page/search/view.jsx +++ b/src/renderer/page/search/view.jsx @@ -5,6 +5,8 @@ import FileTile from 'component/fileTile'; import FileListSearch from 'component/fileListSearch'; import ToolTip from 'component/common/tooltip'; import Page from 'component/page'; +import Icon from 'component/common/icon'; +import * as icons from 'constants/icons'; const MODAL_ANIMATION_TIME = 250; @@ -50,10 +52,11 @@ class SearchPage extends React.PureComponent {
{__('Exact URL')} + > + +
diff --git a/src/renderer/redux/actions/content.js b/src/renderer/redux/actions/content.js index 8dae86f8f..7278beb3d 100644 --- a/src/renderer/redux/actions/content.js +++ b/src/renderer/redux/actions/content.js @@ -42,10 +42,10 @@ export function doFetchFeaturedUris() { }); const success = ({ Uris }) => { - let urisToResolve = []; - Object.keys(Uris).forEach(category => { - urisToResolve = [...urisToResolve, ...Uris[category]]; - }); + const urisToResolve = Object.keys(Uris).reduce( + (resolve, category) => [...resolve, ...Uris[category]], + [] + ); const actions = [ doResolveUris(urisToResolve), diff --git a/src/renderer/redux/actions/publish.js b/src/renderer/redux/actions/publish.js index 2981299fc..270f156b4 100644 --- a/src/renderer/redux/actions/publish.js +++ b/src/renderer/redux/actions/publish.js @@ -6,7 +6,6 @@ import type { UpdatePublishFormAction, PublishParams, } from 'redux/reducers/publish'; -import { CHANNEL_NEW, CHANNEL_ANONYMOUS } from 'constants/claim'; type Action = UpdatePublishFormAction | { type: ACTIONS.CLEAR_PUBLISH }; type PromiseAction = Promise; @@ -86,6 +85,7 @@ export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getStat thumbnail, nsfw, channel, + channelId, title, contentIsFree, price, @@ -104,7 +104,6 @@ export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getStat } } - const channelName = channel === CHANNEL_ANONYMOUS || channel === CHANNEL_NEW ? '' : channel; const fee = contentIsFree || !price.amount ? undefined : { ...price }; const metadata = { @@ -126,7 +125,7 @@ export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getStat const publishPayload = { name, - channel_name: channelName, + channel_id: channelId, bid, metadata, }; diff --git a/src/renderer/redux/actions/rewards.js b/src/renderer/redux/actions/rewards.js index dd4446f3f..581772a3d 100644 --- a/src/renderer/redux/actions/rewards.js +++ b/src/renderer/redux/actions/rewards.js @@ -1,7 +1,7 @@ import * as ACTIONS from 'constants/action_types'; import Lbryio from 'lbryio'; import { doNotify, MODALS } from 'lbry-redux'; -import { selectUnclaimedRewardsByType } from 'redux/selectors/rewards'; +import { selectUnclaimedRewards } from 'redux/selectors/rewards'; import { selectUserIsRewardApproved } from 'redux/selectors/user'; import rewards from 'rewards'; @@ -30,8 +30,8 @@ export function doRewardList() { export function doClaimRewardType(rewardType) { return (dispatch, getState) => { const state = getState(); - const rewardsByType = selectUnclaimedRewardsByType(state); - const reward = rewardsByType[rewardType]; + const unclaimedRewards = selectUnclaimedRewards(state); + const reward = unclaimedRewards.find(ur => ur.reward_type === rewardType); const userIsRewardApproved = selectUserIsRewardApproved(state); if (!reward || reward.transaction_id) { @@ -84,14 +84,14 @@ export function doClaimRewardType(rewardType) { export function doClaimEligiblePurchaseRewards() { return (dispatch, getState) => { const state = getState(); - const rewardsByType = selectUnclaimedRewardsByType(state); + const unclaimedRewards = selectUnclaimedRewards(state); const userIsRewardApproved = selectUserIsRewardApproved(state); if (!userIsRewardApproved || !Lbryio.enabled) { return; } - if (rewardsByType[rewards.TYPE_FIRST_STREAM]) { + if (unclaimedRewards.find(ur => ur.reward_type === rewards.TYPE_FIRST_STREAM)) { dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM)); } else { [rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_FEATURED_DOWNLOAD].forEach(type => { diff --git a/src/renderer/redux/reducers/publish.js b/src/renderer/redux/reducers/publish.js index d20505598..048ca4ac5 100644 --- a/src/renderer/redux/reducers/publish.js +++ b/src/renderer/redux/reducers/publish.js @@ -18,6 +18,7 @@ type PublishState = { language: string, tosAccepted: boolean, channel: string, + channelId: ?string, name: string, nameError: ?string, bid: number, @@ -41,6 +42,7 @@ export type UpdatePublishFormData = { language?: string, tosAccepted?: boolean, channel?: string, + channelId?: string, name?: string, nameError?: string, bid?: number, @@ -66,6 +68,7 @@ export type PublishParams = { thumbnail: ?string, nsfw: boolean, channel: string, + channelId: string, title: string, contentIsFree: boolean, uri: string, @@ -97,6 +100,7 @@ const defaultState: PublishState = { language: 'en', nsfw: false, channel: CHANNEL_ANONYMOUS, + channelId: '', tosAccepted: false, name: '', nameError: undefined, diff --git a/src/renderer/redux/reducers/rewards.js b/src/renderer/redux/reducers/rewards.js index f2f42c004..67f861e6c 100644 --- a/src/renderer/redux/reducers/rewards.js +++ b/src/renderer/redux/reducers/rewards.js @@ -4,7 +4,7 @@ const reducers = {}; const defaultState = { fetching: false, claimedRewardsById: {}, // id => reward - unclaimedRewardsByType: {}, + unclaimedRewards: [], claimPendingByType: {}, claimErrorsByType: {}, }; @@ -17,19 +17,19 @@ reducers[ACTIONS.FETCH_REWARDS_STARTED] = state => reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = (state, action) => { const { userRewards } = action.data; - const unclaimedRewards = {}; + const unclaimedRewards = []; const claimedRewards = {}; userRewards.forEach(reward => { if (reward.transaction_id) { claimedRewards[reward.id] = reward; } else { - unclaimedRewards[reward.reward_type] = reward; + unclaimedRewards.push(reward); } }); return Object.assign({}, state, { claimedRewardsById: claimedRewards, - unclaimedRewardsByType: unclaimedRewards, + unclaimedRewards, fetching: false, }); }; @@ -62,24 +62,21 @@ reducers[ACTIONS.CLAIM_REWARD_STARTED] = (state, action) => { reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = (state, action) => { const { reward } = action.data; + const { unclaimedRewards } = state; - const unclaimedRewardsByType = Object.assign({}, state.unclaimedRewardsByType); - const existingReward = unclaimedRewardsByType[reward.reward_type]; + const index = unclaimedRewards.findIndex(ur => ur.reward_type === reward.reward_type); + unclaimedRewards.splice(index, 1); - const newReward = Object.assign({}, reward, { - reward_title: existingReward.reward_title, - reward_description: existingReward.reward_description, - }); + const { claimedRewardsById } = state; + claimedRewardsById[reward.id] = reward; - const claimedRewardsById = Object.assign({}, state.claimedRewardsById); - claimedRewardsById[reward.id] = newReward; + const newState = { + ...state, + unclaimedRewards: [...unclaimedRewards], + claimedRewardsById: { ...claimedRewardsById }, + }; - const newState = Object.assign({}, state, { - unclaimedRewardsByType, - claimedRewardsById, - }); - - return setClaimRewardState(newState, newReward, false, ''); + return setClaimRewardState(newState, reward, false, ''); }; reducers[ACTIONS.CLAIM_REWARD_FAILURE] = (state, action) => { diff --git a/src/renderer/redux/selectors/rewards.js b/src/renderer/redux/selectors/rewards.js index cb0c83f52..c7fd3ef3d 100644 --- a/src/renderer/redux/selectors/rewards.js +++ b/src/renderer/redux/selectors/rewards.js @@ -1,5 +1,4 @@ import { createSelector } from 'reselect'; -import REWARDS from 'rewards'; const selectState = state => state.rewards || {}; @@ -26,16 +25,7 @@ export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedR }, {}) ); -export const selectUnclaimedRewards = createSelector( - selectUnclaimedRewardsByType, - byType => - Object.values(byType).sort( - (a, b) => - REWARDS.SORT_ORDER.indexOf(a.reward_type) < REWARDS.SORT_ORDER.indexOf(b.reward_type) - ? -1 - : 1 - ) || [] -); +export const selectUnclaimedRewards = createSelector(selectState, state => state.unclaimedRewards); export const selectFetchingRewards = createSelector(selectState, state => !!state.fetching); @@ -65,7 +55,8 @@ const selectClaimRewardError = (state, props) => export const makeSelectClaimRewardError = () => createSelector(selectClaimRewardError, errorMessage => errorMessage); -const selectRewardByType = (state, props) => selectUnclaimedRewardsByType(state)[props.reward_type]; +const selectRewardByType = (state, rewardType) => + selectUnclaimedRewards(state).find(reward => reward.reward_type === rewardType); export const makeSelectRewardByType = () => createSelector(selectRewardByType, reward => reward); diff --git a/src/renderer/rewards.js b/src/renderer/rewards.js index 9905866d3..9da3727a9 100644 --- a/src/renderer/rewards.js +++ b/src/renderer/rewards.js @@ -1,21 +1,6 @@ import { Lbry, doNotify } from 'lbry-redux'; import Lbryio from 'lbryio'; -function rewardMessage(type, amount) { - return { - new_developer: __('You earned %s for registering as a new developer.', amount), - new_user: __('You earned %s LBC new user reward.', amount), - verified_email: __('You earned %s LBC for verifying your email address.', amount), - new_channel: __('You earned %s LBC for creating a publisher identity.', amount), - first_stream: __('You earned %s LBC for streaming your first video.', amount), - many_downloads: __('You earned %s LBC for downloading a bunch of things.', amount), - first_publish: __('You earned %s LBC for making your first publication.', amount), - featured_download: __('You earned %s LBC for watching a featured download.', amount), - referral: __('You earned %s LBC for referring someone.', amount), - youtube_creator: __('You earned %s LBC for syncing your YouTube channel.', amount), - }[type]; -} - const rewards = {}; rewards.TYPE_NEW_DEVELOPER = 'new_developer'; @@ -28,18 +13,6 @@ rewards.TYPE_FIRST_PUBLISH = 'first_publish'; rewards.TYPE_FEATURED_DOWNLOAD = 'featured_download'; rewards.TYPE_REFERRAL = 'referral'; rewards.YOUTUBE_CREATOR = 'youtube_creator'; -rewards.SORT_ORDER = [ - rewards.TYPE_NEW_USER, - rewards.TYPE_CONFIRM_EMAIL, - rewards.TYPE_FIRST_STREAM, - rewards.TYPE_FIRST_CHANNEL, - rewards.TYPE_FIRST_PUBLISH, - rewards.TYPE_FEATURED_DOWNLOAD, - rewards.TYPE_MANY_DOWNLOADS, - rewards.TYPE_REFERRAL, - rewards.TYPE_NEW_DEVELOPER, - rewards.YOUTUBE_CREATOR, -]; rewards.claimReward = type => { function requestReward(resolve, reject, params) { @@ -48,7 +21,8 @@ rewards.claimReward = type => { return; } Lbryio.call('reward', 'new', params, 'post').then(reward => { - const message = rewardMessage(type, reward.reward_amount); + const message = + reward.reward_notification || `You have claimed a ${reward.reward_amount} LBC reward.`; // Display global notice const action = doNotify({ diff --git a/src/renderer/scss/_vars.scss b/src/renderer/scss/_vars.scss index cdfe3ddcf..db8bf3e9e 100644 --- a/src/renderer/scss/_vars.scss +++ b/src/renderer/scss/_vars.scss @@ -35,6 +35,7 @@ $large-breakpoint: 1760px; --color-green-light: #effbe4; --color-green-blue: #2ec1a8; --color-purple: #8165b0; + --color-blue-grey: #203049; /* Colors */ --color-divider: #e3e3e3; @@ -73,6 +74,8 @@ $large-breakpoint: 1760px; --input-copyable-bg: #f6f6f6; --input-copyable-color: var(--color-grey-dark); --input-copyable-border: var(--color-grey); + --input-select-bg-color: var(--color-grey); + --input-select-color: var(--text-color); /* input:disabled */ --input-disabled-border-color: rgba(0, 0, 0, 0.42); @@ -158,9 +161,8 @@ $large-breakpoint: 1760px; --modal-btn-bg-color: var(--btn-bg-alt); // /* Tooltip */ - --tooltip-width: 300px; - --tooltip-bg: var(--color-bg); - --tooltip-color: var(--text-color); + --tooltip-bg: #555; + --tooltip-color: var(--color-white); /* Scrollbar */ --scrollbar-radius: 10px; diff --git a/src/renderer/scss/component/_card.scss b/src/renderer/scss/component/_card.scss index 84e74d3e2..b872a95e0 100644 --- a/src/renderer/scss/component/_card.scss +++ b/src/renderer/scss/component/_card.scss @@ -2,7 +2,6 @@ margin-left: auto; margin-right: auto; border-radius: var(--card-radius); - overflow: auto; user-select: text; display: flex; position: relative; @@ -18,7 +17,6 @@ .card--small { width: var(--card-small-width); - overflow-x: hidden; white-space: normal; .card__media { @@ -87,8 +85,7 @@ align-items: center; .credit-amount, .icon { - margin-top: $spacing-vertical * 1/3; - margin-left: $spacing-vertical * 2/3; + margin: $spacing-vertical * 1/3; } } @@ -127,7 +124,11 @@ color: var(--card-text-color); .icon { - margin-left: $spacing-vertical * 1/3; + margin-top: $spacing-vertical * 1/6; + + &:not(:first-of-type) { + margin: 0 $spacing-vertical * 1/3; + } } } @@ -135,11 +136,6 @@ padding-top: $spacing-vertical * 1/3; } -.card__subtitle--file-info { - display: flex; - align-items: center; -} - .card__subtitle--block { display: block; } @@ -184,6 +180,10 @@ margin-top: $spacing-vertical * 2/3; } +.card__content--extra-padding { + margin-top: $spacing-vertical; +} + .card__subtext-title { color: var(--text-color); font-size: calc(var(--font-size-subtext-multiple) * 1.5em); @@ -240,12 +240,11 @@ } /* - .card-row is used on the discover/subscriptions page + .card-row is used on the discover page It is a list of cards that extend past the right edge of the screen There are left/right arrows to scroll the cards and view hidden content */ .card-row { - overflow: hidden; white-space: nowrap; width: 100%; min-width: var(--card-small-width); @@ -286,6 +285,18 @@ padding-top: $spacing-vertical * 2/3; overflow: hidden; + .card { + display: inline-block; + vertical-align: top; + overflow-y: visible; + // 31 px to handle to padding between cards + width: calc((100% / 4) - 31px); + } + + .card:not(:first-of-type) { + margin-left: 20px; + } + .card:first-of-type { margin-left: $spacing-width; } @@ -324,27 +335,6 @@ } } -.card-row__scrollhouse { - padding-top: $spacing-vertical * 2/3; - overflow: hidden; - - .card { - display: inline-block; - vertical-align: top; - overflow: visible; - // 31 px to handle to padding between cards - width: calc((100% / 4) - 31px); - } - - .card:not(:first-of-type) { - margin-left: 20px; - } - - .card:last-of-type { - margin-right: 20px; - } -} - .card__success-msg { border-left: 2px solid var(--success-msg-border); color: var(--success-msg-color); diff --git a/src/renderer/scss/component/_file-download.scss b/src/renderer/scss/component/_file-download.scss index c4bd7e025..b38d8a520 100644 --- a/src/renderer/scss/component/_file-download.scss +++ b/src/renderer/scss/component/_file-download.scss @@ -1,26 +1,3 @@ -.file-download, -.file-download__overlay { - padding: 5px; -} - .file-download { - position: relative; - background-color: var(--color-black); - border-radius: var(--btn-radius); - color: var(--color-download); - font-size: 12px; - opacity: 0.8; - font-family: 'metropolis-medium'; -} - -.file-download__overlay { - background: var(--color-download); - color: var(--color-download-overlay); - border-radius: var(--btn-radius); - position: absolute; - white-space: nowrap; - overflow: hidden; - z-index: 1; - top: 0px; - left: 0px; + font-size: 0.8em; } diff --git a/src/renderer/scss/component/_file-list.scss b/src/renderer/scss/component/_file-list.scss index 72cee988f..30b2de303 100644 --- a/src/renderer/scss/component/_file-list.scss +++ b/src/renderer/scss/component/_file-list.scss @@ -14,6 +14,10 @@ .file-list__header { margin-top: $spacing-vertical * 4/3; font-size: 18px; + + .tooltip { + margin-left: 5px; + } } .file-tile { diff --git a/src/renderer/scss/component/_form-field.scss b/src/renderer/scss/component/_form-field.scss index 2be99b8ab..9dd03b5d7 100644 --- a/src/renderer/scss/component/_form-field.scss +++ b/src/renderer/scss/component/_form-field.scss @@ -62,6 +62,10 @@ width: 35px; } + input.paginate-channel { + width: 35px; + } + &.form-field--auto-height { height: auto; } @@ -100,10 +104,23 @@ padding-left: $spacing-vertical * 1/3; } +.form-field__select { + min-width: 60px; + height: 30px; + border-radius: 8px; + background-color: var(--input-select-bg-color); + font: normal 12px/30px 'metropolis-medium'; + color: var(--input-select-color); + + &:disabled { + opacity: 0.5; + } +} + // Not sure if I like these // Maybe this should be in gui.scss? .input--price-amount { - width: 60px; + width: 80px; } .input--address { diff --git a/src/renderer/scss/component/_tooltip.scss b/src/renderer/scss/component/_tooltip.scss index 504880b09..3e32a7fbe 100644 --- a/src/renderer/scss/component/_tooltip.scss +++ b/src/renderer/scss/component/_tooltip.scss @@ -1,30 +1,97 @@ -@import '../mixin/link.scss'; - .tooltip { position: relative; - padding: 0 $spacing-vertical / 3; - font-size: 12px; + display: inline-block; } -.tooltip__body { +// When there is a label for the tooltip and not just using a button or icon +.tooltip.tooltip--label { + font-size: 12px; + padding-left: $spacing-vertical * 1/3; + + .tooltip__body { + margin-top: 5px; + } +} + +.tooltip.tooltip--icon { + margin-top: 5px; +} + +/* Tooltip text */ +.tooltip .tooltip__body { + background-color: var(--tooltip-bg); + font-family: 'metropolis-medium'; + font-size: 12px; + color: var(--tooltip-color); + border-radius: 8px; position: absolute; z-index: 1; - left: 50%; - margin-left: calc(var(--tooltip-width) * -1 / 2); - white-space: normal; - box-sizing: border-box; - padding: $spacing-vertical / 2; - width: var(--tooltip-width); - color: var(--tooltip-color); - background-color: var(--tooltip-bg); - font-size: calc(var(--font-size) * 7 / 8); - line-height: var(--font-line-height); - box-shadow: var(--box-shadow-layer); - border-radius: var(--card-radius); + width: 200px; + text-align: center; + white-space: pre-wrap; + padding: $spacing-vertical * 1/3; + visibility: hidden; } -.tooltip__link { - font-size: calc(var(--font-size) * 3 / 4); - margin-left: var(--button-padding); - vertical-align: middle; +.tooltip .tooltip__body::after { + content: ' '; + width: 0; + height: 0; + position: absolute; + border-width: 5px; + border-style: solid; +} + +.tooltip.tooltip--top .tooltip__body { + bottom: 100%; + left: 50%; + margin-left: -100px; + + &::after { + top: 100%; + left: 50%; + margin-left: -5px; + border-color: var(--tooltip-bg) transparent transparent transparent; + } +} + +.tooltip.tooltip--right .tooltip__body { + margin-top: -5px; + margin-left: 10px; + + &::after { + top: 17px; + right: 100%; /* To the left of the tooltip */ + margin-top: -5px; + border-color: transparent var(--tooltip-bg) transparent transparent; + } +} + +.tooltip.tooltip--bottom .tooltip__body { + top: 90%; + left: 50%; + margin-left: -100px; + + &::after { + bottom: 100%; + left: 50%; + margin-left: -5px; + border-color: transparent transparent var(--tooltip-bg) transparent; + } +} + +.tooltip.tooltip--left .tooltip__body { + top: -5px; + right: 105%; + + &::after { + top: 17px; + left: 100%; + margin-top: -5px; + border-color: transparent transparent transparent var(--tooltip-bg); + } +} + +.tooltip:hover .tooltip__body { + visibility: visible; } diff --git a/src/renderer/scss/mixin/link.scss b/src/renderer/scss/mixin/link.scss deleted file mode 100644 index df55e49ba..000000000 --- a/src/renderer/scss/mixin/link.scss +++ /dev/null @@ -1,28 +0,0 @@ -@mixin text-link($color: var(--color-primary), $hover-opacity: 0.7) { - .icon { - &:first-child { - padding-right: 5px; - } - &:last-child:not(:only-child) { - padding-left: 5px; - } - } - - &:not(.no-underline) { - text-decoration: underline; - .icon { - text-decoration: none; - } - } - &:hover { - opacity: $hover-opacity; - transition: opacity var(--transition-duration) var(--transition-type); - text-decoration: underline; - .icon { - text-decoration: none; - } - } - - color: $color; - cursor: pointer; -} diff --git a/src/renderer/util/contextMenu.js b/src/renderer/util/contextMenu.js new file mode 100644 index 000000000..79f47da5b --- /dev/null +++ b/src/renderer/util/contextMenu.js @@ -0,0 +1,68 @@ +import { clipboard, remote } from 'electron'; +import isDev from 'electron-is-dev'; + +function injectDevelopmentTemplate(event, templates) { + if (!isDev) return templates; + const { screenX, screenY } = event; + const separator = { type: 'separator' }; + const developmentTemplateAddition = [ + { + label: 'Inspect element', + accelerator: 'CmdOrCtrl+Shift+I', + click: () => { + remote.getCurrentWindow().inspectElement(screenX, screenY); + }, + }, + ]; + if (templates.length > 0) { + templates.push(separator); + } + templates.push(...developmentTemplateAddition); + return templates; +} + +export function openContextMenu(event, templates = [], addDefaultTemplates = true) { + if (addDefaultTemplates) { + const { value } = event.target; + const inputTemplates = [ + { label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' }, + { label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' }, + { + label: 'Paste', + accelerator: 'CmdOrCtrl+V', + role: 'paste', + enabled: clipboard.readText().length > 0, + }, + { + label: 'Select All', + accelerator: 'CmdOrCtrl+A', + role: 'selectall', + enabled: !!value, + }, + ]; + templates.push(...inputTemplates); + } + + injectDevelopmentTemplate(event, templates); + remote.Menu.buildFromTemplate(templates).popup(); +} +export function openCopyLinkMenu(text, event) { + const templates = [ + { + label: 'Copy link', + click: () => { + clipboard.writeText(text); + }, + }, + ]; + openContextMenu(event, templates, false); +} + +export function initContextMenu(event) { + const { type } = event.target; + if (event.target.matches('input') && (type === 'text' || type === 'number')) { + openContextMenu(event); + } else { + event.preventDefault(); + } +} diff --git a/static/themes/dark.css b/static/themes/dark.css index 7796724ef..9bf8cac02 100644 --- a/static/themes/dark.css +++ b/static/themes/dark.css @@ -7,7 +7,7 @@ --color-help: #8696AF; --color-download: rgba(255, 255, 255, 0.75); --color-download-overlay: var(--color-black); - --color-bg: #203049; + --color-bg: var(--color-blue-grey); --color-bg-alt: #2D3D56; --color-placeholder: var(--color-bg-alt); @@ -26,9 +26,11 @@ --input-border-size: 1px; --input-border-color: rgba(255,255,255, 0.5); --input-hover-border-color: rgba(255, 255, 255, 1); - --input-copyable-bg: #203049; + --input-copyable-bg: var(--color-blue-grey); --input-copyable-color: #8696AF; --input-copyable-border: #53637C; + --input-select-bg-color: var(--color-bg-alt); + --input-select-color: var(--color-white); /* input:disabled */ --input-disabled-border-color: rgba(255, 255, 255, 0.42); diff --git a/yarn.lock b/yarn.lock index ffa56a39f..27fb511b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5837,9 +5837,9 @@ lazy-val@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.3.tgz#bb97b200ef00801d94c317e29dc6ed39e31c5edc" -lbry-redux@lbryio/lbry-redux#30c18725d8c6c141c30c57f0a324d0abb8963b99: +lbry-redux@lbryio/lbry-redux#c41899e78415cae6fcb7bfca0e6ba48bb6bfe6c4: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/30c18725d8c6c141c30c57f0a324d0abb8963b99" + resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/c41899e78415cae6fcb7bfca0e6ba48bb6bfe6c4" dependencies: proxy-polyfill "0.1.6" reselect "^3.0.0"