From b7f23aa0dd03bb1d84a8eb4b29e798a4cc3e3fe2 Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Thu, 12 Jan 2017 21:03:34 -0500 Subject: [PATCH] big refactor of file actions/buttons/tiles --- js/component/common.js | 2 +- js/component/file-actions.js | 229 +++++++++++++++++++++++++ js/component/file-tile.js | 227 +++++++++++-------------- js/component/link.js | 266 ------------------------------ js/component/menu.js | 85 ++++++---- js/lbry.js | 53 +++++- js/page/discover.js | 75 ++------- js/page/my_files.js | 5 +- js/page/show.js | 8 +- scss/_canvas.scss | 21 +-- scss/_global.scss | 5 +- scss/_gui.scss | 108 +++--------- scss/all.scss | 5 +- scss/component/_file-actions.scss | 29 ++++ scss/component/_file-tile.scss | 27 +++ scss/component/_menu.scss | 21 +++ scss/{ => component}/_table.scss | 2 + 17 files changed, 563 insertions(+), 605 deletions(-) create mode 100644 js/component/file-actions.js create mode 100644 scss/component/_file-actions.scss create mode 100644 scss/component/_file-tile.scss create mode 100644 scss/component/_menu.scss rename scss/{ => component}/_table.scss (97%) diff --git a/js/component/common.js b/js/component/common.js index 0c01e7293..40fb0ce7e 100644 --- a/js/component/common.js +++ b/js/component/common.js @@ -152,6 +152,6 @@ export let Thumbnail = React.createClass({ this._isMounted = false; }, render: function() { - return + return }, }); diff --git a/js/component/file-actions.js b/js/component/file-actions.js new file mode 100644 index 000000000..0a347bdbc --- /dev/null +++ b/js/component/file-actions.js @@ -0,0 +1,229 @@ +import React from 'react'; +import lbry from '../lbry.js'; +import {Link} from '../component/link.js'; +import {Icon} from '../component/common.js'; +import Modal from './modal.js'; +import FormField from './form.js'; +import {DropDownMenu, DropDownMenuItem} from './menu.js'; + +let WatchLink = React.createClass({ + propTypes: { + streamName: React.PropTypes.string, + }, + handleClick: function() { + this.setState({ + loading: true, + }) + lbry.getCostInfoForName(this.props.streamName, ({cost}) => { + lbry.getBalance((balance) => { + if (cost > balance) { + this.setState({ + modal: 'notEnoughCredits', + loading: false, + }); + } else { + window.location = '?watch=' + this.props.streamName; + } + }); + }); + }, + getInitialState: function() { + return { + modal: null, + loading: false, + }; + }, + closeModal: function() { + this.setState({ + modal: null, + }); + }, + render: function() { + return ( +
+ + + You don't have enough LBRY credits to pay for this stream. + +
+ ); + } +}); + +export let FileActions = React.createClass({ + _isMounted: false, + _fileInfoSubscribeId: null, + + propTypes: { + streamName: React.PropTypes.string, + sdHash: React.PropTypes.string, + metadata: React.PropTypes.object, + path: React.PropTypes.string, + hidden: React.PropTypes.bool, + onRemoveConfirmed: React.PropTypes.func, + deleteChecked: React.PropTypes.bool, + }, + getInitialState: function() { + return { + fileInfo: null, + modal: null, + menuOpen: false, + deleteChecked: false, + attemptingDownload: false, + attemptingRemove: false, + } + }, + onFileInfoUpdate: function(fileInfo) { + if (this._isMounted) { + this.setState({ + fileInfo: fileInfo ? fileInfo : false, + attemptingDownload: fileInfo ? false : this.state.attemptingDownload + }); + } + }, + tryDownload: function() { + this.setState({ + attemptingDownload: true, + attemptingRemove: false + }); + lbry.getCostInfoForName(this.props.streamName, ({cost}) => { + lbry.getBalance((balance) => { + if (cost > balance) { + this.setState({ + modal: 'notEnoughCredits', + attemptingDownload: false, + }); + } else { + lbry.getStream(this.props.streamName, (streamInfo) => { + if (streamInfo === null || typeof streamInfo !== 'object') { + this.setState({ + modal: 'timedOut', + attemptingDownload: false, + }); + } + }); + } + }); + }); + }, + closeModal: function() { + this.setState({ + modal: null, + }) + }, + onDownloadClick: function() { + if (!this.state.fileInfo && !this.state.attemptingDownload) { + this.tryDownload(); + } + }, + onOpenClick: function() { + if (this.state.fileInfo && this.state.fileInfo.completed) { + lbry.openFile(this.state.fileInfo.download_path); + } + }, + handleDeleteCheckboxClicked: function(event) { + this.setState({ + deleteChecked: event.target.checked, + }); + }, + handleRevealClicked: function() { + if (this.state.fileInfo && this.state.fileInfo.download_path) { + lbry.revealFile(this.state.fileInfo.download_path); + } + }, + handleRemoveClicked: function() { + this.setState({ + modal: 'confirmRemove', + }); + }, + handleRemoveConfirmed: function() { + lbry.deleteFile(this.props.sdHash || this.props.streamName, this.state.deleteChecked); + if (this.props.onRemoveConfirmed) { + this.props.onRemoveConfirmed(); + } + this.setState({ + modal: null, + fileInfo: false, + attemptingRemove: true, + attemptingDownload: false + }); + }, + openMenu: function() { + this.setState({ + menuOpen: !this.state.menuOpen, + }); + }, + componentDidMount: function() { + this._isMounted = true; + + if ('sdHash' in this.props) { + alert('render by sd hash is broken'); + lbry.fileInfoSubscribeByStreamHash(this.props.sdHash, this.fileInfoU); + } else if ('streamName' in this.props) { + this._fileInfoSubscribeId = lbry.fileInfoSubscribeByName(this.props.streamName, this.onFileInfoUpdate); + } else { + throw new Error("No stream name or sd hash passed to FileTile"); + } + }, + componentWillUnmount: function() { + this._isMounted = false; + if (this._fileInfoSubscribeId) { + lbry.fileInfoUnsubscribe(this.props.name, this._fileInfoSubscribeId); + } + }, + render: function() { + if (this.state.fileInfo === null) + { + return
; + } + const openInFolderMessage = window.navigator.platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder', + showMenu = !this.state.attemptingRemove && this.state.fileInfo !== null; + + let linkBlock; + if (this.state.attemptingRemove || (this.state.fileInfo === false && !this.state.attemptingDownload)) { + linkBlock = ; + } else if (this.state.attemptingDownload || !this.state.fileInfo.completed) { + const + progress = this.state.fileInfo ? this.state.fileInfo.written_bytes / this.state.fileInfo.total_bytes * 100 : 0, + label = this.state.fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...', + labelWithIcon = {label}; + + linkBlock = +
+
{labelWithIcon}
+ {labelWithIcon} +
; + } else { + linkBlock = ; + } + + return ( +
+ {this.props.metadata.content_type.startsWith('video/') ? : null} + {this.state.fileInfo !== null || this.state.fileInfo.isMine ? +
{linkBlock}
+ : null} + { showMenu ? + + + + : '' } + + You don't have enough LBRY credits to pay for this stream. + + + LBRY was unable to download the stream lbry://{this.props.streamName}. + + +

Are you sure you'd like to remove {this.props.metadata.title} from LBRY?

+ + +
+
+ ); + } +}); \ No newline at end of file diff --git a/js/component/file-tile.js b/js/component/file-tile.js index 2477e0841..293a63c9c 100644 --- a/js/component/file-tile.js +++ b/js/component/file-tile.js @@ -1,188 +1,149 @@ import React from 'react'; import lbry from '../lbry.js'; -import {Link, DownloadLink, WatchLink} from '../component/link.js'; +import {Link} from '../component/link.js'; +import {FileActions} from '../component/file-actions.js'; import {Thumbnail, TruncatedText, CreditAmount} from '../component/common.js'; -let FileTile = React.createClass({ +let FilePrice = React.createClass({ _isMounted: false, - _fileInfoCheckInterval: 5000, propTypes: { - metadata: React.PropTypes.object.isRequired, - fileInfo: React.PropTypes.string, - name: React.PropTypes.string, - sdHash: React.PropTypes.string, - available: React.PropTypes.bool, - isMine: React.PropTypes.bool, - local: React.PropTypes.bool, - cost: React.PropTypes.number, - costIncludesData: React.PropTypes.bool, - hideOnRemove: React.PropTypes.bool, + name: React.PropTypes.string }, - updateFileInfo: function(progress=null) { - const updateFileInfoCallback = ((fileInfo) => { - if (!this._isMounted || 'fileInfo' in this.props) { - /** - * The component was unmounted, or a file info data structure has now been provided by the - * containing component. - */ - return; - } - this.setState({ - fileInfo: fileInfo || null, - local: !!fileInfo, - }); - - setTimeout(() => { this.updateFileInfo() }, this._fileInfoCheckInterval); - }); - - if ('sdHash' in this.props) { - lbry.getFileInfoBySdHash(this.props.sdHash, updateFileInfoCallback); - this.getIsMineIfNeeded(this.props.sdHash); - } else if ('name' in this.props) { - lbry.getFileInfoByName(this.props.name, (fileInfo) => { - this.getIsMineIfNeeded(fileInfo.sd_hash); - - updateFileInfoCallback(fileInfo); - }); - } else { - throw new Error("No progress, stream name or sd hash passed to FileTile"); - } - }, - getIsMineIfNeeded: function(sdHash) { - if (this.state.isMine !== null) { - // The info was already provided by this.props.isMine - return; - } - - lbry.getMyClaims((claimsInfo) => { - for (let {value} of claimsInfo) { - if (JSON.parse(value).sources.lbry_sd_hash == sdHash) { - this.setState({ - isMine: true, - }); - return; - } - } - - this.setState({ - isMine: false, - }); - }); - }, getInitialState: function() { return { - downloading: false, - removeConfirmed: false, - isHovered: false, cost: null, costIncludesData: null, - fileInfo: 'fileInfo' in this.props ? this.props.fileInfo : null, - isMine: 'isMine' in this.props ? this.props.isMine : null, - local: 'local' in this.props ? this.props.local : null, } }, - getDefaultProps: function() { - return { - compact: false, - hideOnRemove: false, - } - }, - handleMouseOver: function() { - this.setState({ - isHovered: true, - }); - }, - handleMouseOut: function() { - this.setState({ - isHovered: false, - }); - }, - handleRemoveConfirmed: function() { - this.setState({ - removeConfirmed: true, - }); - }, - componentWillMount: function() { - this.updateFileInfo(); - if ('cost' in this.props) { - this.setState({ - cost: this.props.cost, - costIncludesData: this.props.costIncludesData, - }); - } else { - lbry.getCostInfoForName(this.props.name, ({cost, includesData}) => { + componentDidMount: function() { + this._isMounted = true; + + lbry.getCostInfoForName(this.props.name, ({cost, includesData}) => { + if (this._isMounted) { this.setState({ cost: cost, costIncludesData: includesData, }); + } + }); + }, + + componentWillUnmount: function() { + this._isMounted = false; + }, + + render: function() { + if (this.state.cost === null) + { + return null; + } + + return ( + + + + ); + } +}); + +let FileTile = React.createClass({ + _isMounted: false, + + propTypes: { + name: React.PropTypes.string, + sdHash: React.PropTypes.string, + showPrice: React.PropTypes.bool, + obscureNsfw: React.PropTypes.bool, + hideOnRemove: React.PropTypes.bool + }, + + getInitialState: function() { + return { + metadata: null, + title: null, + showNsfwHelp: false, + isRemoved: false + } + }, + getDefaultProps: function() { + return { + hideOnRemove: false, + obscureNsfw: !lbry.getClientSetting('showNsfw'), + showPrice: true + } + }, + handleMouseOver: function() { + if (this.props.obscureNsfw && this.state.metadata && this.state.metadata.nsfw) { + this.setState({ + showNsfwHelp: true, }); } }, + handleMouseOut: function() { + if (this.state.showNsfwHelp) { + this.setState({ + showNsfwHelp: false, + }); + } + }, + onRemove: function() { + this.setState({ + isRemoved: true, + }); + }, componentDidMount: function() { this._isMounted = true; + + lbry.resolveName(this.props.name, (metadata) => { + if (this._isMounted) { + this.setState({ + metadata: metadata, + title: metadata && metadata.title ? metadata.title : ('lbry://' + this.props.name), + }); + } + }); }, componentWillUnmount: function() { this._isMounted = false; }, render: function() { - if (this.state.isMine === null || this.state.local === null || - (this.props.hideOnRemove && this.state.removeConfirmed)) { + if (this.state.metadata === null || (this.props.hideOnRemove && this.state.isRemoved)) { return null; } - const obscureNsfw = !lbry.getClientSetting('showNsfw') && this.props.metadata.nsfw; + const obscureNsfw = this.props.obscureNsfw && this.state.metadata.nsfw; - let downloadLinkExtraProps = {}; - if (this.state.fileInfo === null) { - downloadLinkExtraProps.state = 'not-started'; - } else if (!this.state.fileInfo.completed) { - downloadLinkExtraProps.state = 'downloading'; - - const {written_bytes, total_bytes, path} = this.state.fileInfo; - downloadLinkExtraProps.progress = written_bytes / total_bytes; - } else { - downloadLinkExtraProps.state = 'done'; - downloadLinkExtraProps.path = this.state.fileInfo.download_path; - } return ( -
+
- +
- {this.state.cost !== null && !this.state.local - ? - - + { this.props.showPrice + ? : null} -

+

- - {this.props.metadata.title} + + {this.state.metadata.title}

-
- {this.props.metadata.content_type.startsWith('video/') ? : null} - {!this.props.isMine - ? - : null} -
+

- {this.props.metadata.description} + {this.state.metadata.description}

- {obscureNsfw && this.state.isHovered + {this.state.showNsfwHelp ?

This content is Not Safe For Work. diff --git a/js/component/link.js b/js/component/link.js index 28f348d24..b0d7f53ec 100644 --- a/js/component/link.js +++ b/js/component/link.js @@ -1,11 +1,6 @@ import React from 'react'; -import lbry from '../lbry.js'; -import FormField from './form.js'; -import Modal from './modal.js'; -import {Menu, MenuItem} from './menu.js'; import {Icon, ToolTip} from './common.js'; - export let Link = React.createClass({ propTypes: { label: React.PropTypes.string, @@ -110,264 +105,3 @@ export let ToolTipLink = React.createClass({ ); } }); - -export let DropDown = React.createClass({ - propTypes: { - onCaretClick: React.PropTypes.func, - }, - handleCaretClicked: function(event) { - /** - * The menu handles caret clicks via a window event listener, so we just need to prevent clicks - * on the caret from bubbling up to the link - */ - this.setState({ - menuOpen: !this.state.menuOpen, - }); - event.stopPropagation(); - return false; - }, - closeMenu: function(event) { - this.setState({ - menuOpen: false, - }); - }, - getInitialState: function() { - return { - menuOpen: false, - }; - }, - render: function() { - const {onCaretClick, ...other} = this.props; - return ( -

- - {this.props.label} - - - {this.state.menuOpen - ? - {this.props.children} - - : null} -
- ); - } -}); - -export let DownloadLink = React.createClass({ - propTypes: { - type: React.PropTypes.string, - streamName: React.PropTypes.string, - sdHash: React.PropTypes.string, - metadata: React.PropTypes.object, - label: React.PropTypes.string, - button: React.PropTypes.string, - state: React.PropTypes.oneOf(['not-started', 'downloading', 'done']), - progress: React.PropTypes.number, - path: React.PropTypes.string, - hidden: React.PropTypes.bool, - deleteChecked: React.PropTypes.bool, - onRemoveConfirmed: React.PropTypes.func, - }, - tryDownload: function() { - this.setState({ - attemptingDownload: true, - }); - lbry.getCostInfoForName(this.props.streamName, ({cost}) => { - lbry.getBalance((balance) => { - if (cost > balance) { - this.setState({ - modal: 'notEnoughCredits', - attemptingDownload: false, - }); - } else { - lbry.getStream(this.props.streamName, (streamInfo) => { - if (streamInfo === null || typeof streamInfo !== 'object') { - this.setState({ - modal: 'timedOut', - attemptingDownload: false, - }); - } else { - this.setState({ - filePath: streamInfo.path, - attemptingDownload: false, - }); - } - }); - } - }); - }); - }, - openMenu: function() { - this.setState({ - menuOpen: !this.state.menuOpen, - }); - }, - handleDeleteCheckboxClicked: function(event) { - this.setState({ - deleteChecked: event.target.checked, - }); - }, - handleRevealClicked: function() { - lbry.revealFile(this.props.path); - }, - handleRemoveClicked: function() { - this.setState({ - modal: 'confirmRemove', - }); - }, - handleRemoveConfirmed: function() { - lbry.deleteFile(this.props.sdHash || this.props.streamName, this.state.deleteChecked); - if (this.props.onRemoveConfirmed) { - this.props.onRemoveConfirmed(); - } - this.setState({ - modal: null, - attemptingRemove: true, - }); - }, - getDefaultProps: function() { - return { - state: 'not-started', - hideOnDelete: false, - } - }, - getInitialState: function() { - return { - filePath: null, - modal: null, - menuOpen: false, - deleteChecked: false, - attemptingDownload: false, - attemptingRemove: false, - } - }, - closeModal: function() { - this.setState({ - modal: null, - }) - }, - handleClick: function() { - if (this.props.state == 'not-started') { - this.tryDownload(); - } else if (this.props.state == 'done') { - lbry.openFile(this.props.path); - } - }, - render: function() { - const openInFolderMessage = window.navigator.platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder'; - - const dropDownItems = [ - , - , - ]; - - let linkBlock; - if (this.state.attemptingRemove || this.props.state == 'not-started') { - linkBlock = ; - } else if (this.state.attemptingDownload) { - linkBlock = - } else if (this.props.state == 'downloading') { - const label = `${parseInt(this.props.progress * 100)}% complete`; - linkBlock = ( - - - {dropDownItems} - - - {dropDownItems} - - - ); - } else if (this.props.state == 'done') { - linkBlock = ( - - {dropDownItems} - - ); - } else { - throw new Error(`Unknown download state ${this.props.state} passed to DownloadLink`); - } - - return ( - - {linkBlock} - - You don't have enough LBRY credits to pay for this stream. - - - LBRY was unable to download the stream lbry://{this.props.streamName}. - - -

Are you sure you'd like to remove {this.props.metadata.title} from LBRY?

- - -
-
- ); - } -}); - -export let WatchLink = React.createClass({ - propTypes: { - type: React.PropTypes.string, - streamName: React.PropTypes.string, - label: React.PropTypes.string, - button: React.PropTypes.string, - hidden: React.PropTypes.bool, - }, - handleClick: function() { - this.setState({ - loading: true, - }) - lbry.getCostInfoForName(this.props.streamName, ({cost}) => { - lbry.getBalance((balance) => { - if (cost > balance) { - this.setState({ - modal: 'notEnoughCredits', - loading: false, - }); - } else { - window.location = '?watch=' + this.props.streamName; - } - }); - }); - }, - getInitialState: function() { - return { - modal: null, - loading: false, - }; - }, - closeModal: function() { - this.setState({ - modal: null, - }); - }, - getDefaultProps: function() { - return { - icon: 'icon-play', - label: 'Watch', - } - }, - render: function() { - return ( -
- - - You don't have enough LBRY credits to pay for this stream. - -
- ); - } -}); diff --git a/js/component/menu.js b/js/component/menu.js index cf76ec047..618f3165c 100644 --- a/js/component/menu.js +++ b/js/component/menu.js @@ -1,35 +1,8 @@ import React from 'react'; -import ReactDOM from 'react-dom'; import {Icon} from './common.js'; +import {Link} from '../component/link.js'; -export let Menu = React.createClass({ - propTypes: { - onClickOut: React.PropTypes.func.isRequired, - }, - handleWindowClick: function(e) { - if (!this._div.contains(e.target)) { - // Menu is open and user clicked outside of it - this.props.onClickOut(); - } - }, - componentDidMount: function() { - window.addEventListener('click', this.handleWindowClick, false); - }, - componentWillUnmount: function() { - window.removeEventListener('click', this.handleWindowClick, false); - }, - render: function() { - const {onClickOut, ...other} = this.props; - return ( -
this._div = div} className={'menu ' + (this.props.className || '')} - {... other}> - {this.props.children} -
- ); - } -}); - -export let MenuItem = React.createClass({ +export let DropDownMenuItem = React.createClass({ propTypes: { href: React.PropTypes.string, label: React.PropTypes.string, @@ -45,7 +18,7 @@ export let MenuItem = React.createClass({ var icon = (this.props.icon ? : null); return ( - {this.props.iconPosition == 'left' ? icon : null} {this.props.label} @@ -54,3 +27,55 @@ export let MenuItem = React.createClass({ ); } }); + +export let DropDownMenu = React.createClass({ + _isWindowClickBound: false, + _menuDiv: null, + + getInitialState: function() { + return { + menuOpen: false, + }; + }, + componentWillUnmount: function() { + if (this._isWindowClickBound) { + window.removeEventListener('click', this.handleWindowClick, false); + } + }, + onMenuIconClick: function() { + this.setState({ + menuOpen: !this.state.menuOpen, + }); + if (!this.state.menuOpen && !this._isWindowClickBound) { + this._isWindowClickBound = true; + window.addEventListener('click', this.handleWindowClick, false); + } + return false; + }, + handleWindowClick: function(e) { + if (this.state.menuOpen && + (!this._menuDiv || !this._menuDiv.contains(e.target))) { + console.log('menu closing disabled due to auto close on click, fix me'); + return; + this.setState({ + menuOpen: false + }); + } + }, + render: function() { + if (!this.state.menuOpen && this._isWindowClickBound) { + this._isWindowClickBound = false; + window.removeEventListener('click', this.handleWindowClick, false); + } + return ( +
+ this._menuButton = span} icon="icon-ellipsis-v" onClick={this.onMenuIconClick} /> + {this.state.menuOpen + ?
this._menuDiv = div} className="menu"> + {this.props.children} +
+ : null} +
+ ); + } +}); \ No newline at end of file diff --git a/js/lbry.js b/js/lbry.js index c53b00f28..49abcaa95 100644 --- a/js/lbry.js +++ b/js/lbry.js @@ -132,7 +132,7 @@ lbry.getNewAddress = function(callback) { lbry.call('get_new_address', {}, callback); } -lbry.checkAddressIsMine = function(address, callback) { +lbry.checkAddressIsMine = function(address, callback) { lbry.call('address_is_mine', {address: address}, callback); } @@ -457,5 +457,56 @@ lbry.stop = function(callback) { lbry.call('stop', {}, callback); }; +lbry.fileInfo = {}; +lbry._fileInfoSubscribeIdCounter = 0; +lbry._fileInfoSubscribeCallbacks = {}; +lbry._fileInfoSubscribeInterval = 5000; +lbry._claimIdOwnershipCache = {}; // should be claimId!!! But not + + +lbry._updateClaimOwnershipCache = function(claimId) { + lbry.getMyClaims((claimsInfo) => { + lbry._claimIdOwnershipCache[claimId] = !!claimsInfo.reduce(function(match, claimInfo) { + return match || claimInfo.claim_id == claimId; + }); + }); +}; + +lbry._updateSubscribedFileInfoByName = function(name) { + lbry.getFileInfoByName(name, (fileInfo) => { + if (fileInfo) { + if (this._claimIdOwnershipCache[fileInfo.claim_id] === undefined) { + lbry._updateClaimOwnershipCache(fileInfo.claim_id); + } + fileInfo.isMine = !!this._claimIdOwnershipCache[fileInfo.claim_id]; + } + this._fileInfoSubscribeCallbacks[name].forEach(function(callback) { + callback(fileInfo); + }); + }); + setTimeout(() => { this._updateSubscribedFileInfoByName(name) }, lbry._fileInfoSubscribeInterval); +} + +lbry.fileInfoSubscribeByName = function(name, callback) { + if (!lbry._fileInfoSubscribeCallbacks[name]) + { + lbry._fileInfoSubscribeCallbacks[name] = []; + } + + const subscribeId = ++lbry._fileInfoSubscribeIdCounter; + lbry._fileInfoSubscribeCallbacks[name][subscribeId] = callback; + lbry._updateSubscribedFileInfoByName(name); + return subscribeId; +} + +// lbry.fileInfoSubscribeByStreamHash = function(sdHash, callback) { +// lbry.getFileInfoBySdHash(this.props.sdHash, this.updateFileInfoCallback); +// this.getIsMineIfNeeded(this.props.sdHash); +// setTimeout(() => { this.updateFileInfo() }, this._fileInfoCheckInterval); +// } + +lbry.fileInfoUnsubscribe = function(name, subscribeId) { + delete lbry._fileInfoSubscribeCallbacks[name][subscribeId]; +} export default lbry; diff --git a/js/page/discover.js b/js/page/discover.js index 3c79d96c5..873e36f5f 100644 --- a/js/page/discover.js +++ b/js/page/discover.js @@ -2,8 +2,8 @@ import React from 'react'; import lbry from '../lbry.js'; import lighthouse from '../lighthouse.js'; import FileTile from '../component/file-tile.js'; -import {Link, ToolTipLink, DownloadLink, WatchLink} from '../component/link.js'; -import {Thumbnail, CreditAmount, TruncatedText, BusyMessage} from '../component/common.js'; +import {Link, ToolTipLink} from '../component/link.js'; +import {BusyMessage} from '../component/common.js'; var fetchResultsStyle = { color: '#888', @@ -53,57 +53,6 @@ var SearchResults = React.createClass({ } }); -var featuredContentItemContainerStyle = { - position: 'relative', -}; - -var FeaturedContentItem = React.createClass({ - resolveSearch: false, - - propTypes: { - name: React.PropTypes.string, - }, - - getInitialState: function() { - return { - metadata: null, - title: null, - cost: null, - overlayShowing: false, - }; - }, - - componentWillUnmount: function() { - this.resolveSearch = false; - }, - - componentDidMount: function() { - this._isMounted = true; - - lbry.resolveName(this.props.name, (metadata) => { - if (!this._isMounted) { - return; - } - - this.setState({ - metadata: metadata, - title: metadata && metadata.title ? metadata.title : ('lbry://' + this.props.name), - }); - }); - }, - - render: function() { - if (this.state.metadata === null) { - // Still waiting for metadata, skip render - return null; - } - - return (
- -
); - } -}); - var featuredContentLegendStyle = { fontSize: '12px', color: '#aaa', @@ -116,21 +65,21 @@ var FeaturedContent = React.createClass({

Featured Content

- - - - - + + + + +

Community Content

- - - - - + + + + +
); diff --git a/js/page/my_files.js b/js/page/my_files.js index a26308cf4..d3f488eab 100644 --- a/js/page/my_files.js +++ b/js/page/my_files.js @@ -178,7 +178,7 @@ var MyFilesPage = React.createClass({ seenUris[lbry_uri] = true; - content.push(); } @@ -199,5 +199,4 @@ var MyFilesPage = React.createClass({ } }); - -export default MyFilesPage; +export default MyFilesPage; \ No newline at end of file diff --git a/js/page/show.js b/js/page/show.js index 7486b0c93..0bd1dde57 100644 --- a/js/page/show.js +++ b/js/page/show.js @@ -2,7 +2,8 @@ import React from 'react'; import lbry from '../lbry.js'; import lighthouse from '../lighthouse.js'; import {CreditAmount, Thumbnail} from '../component/common.js'; -import {Link, DownloadLink, WatchLink} from '../component/link.js'; +import {FileActions} from '../component/file-actions.js'; +import {Link} from '../component/link.js'; var formatItemImgStyle = { maxWidth: '100%', @@ -62,10 +63,7 @@ var FormatItem = React.createClass({
-
- {mediaType == 'video' ? : null} - -
+
diff --git a/scss/_canvas.scss b/scss/_canvas.scss index fd24a2236..7a6184a95 100644 --- a/scss/_canvas.scss +++ b/scss/_canvas.scss @@ -56,7 +56,7 @@ $drawer-width: 240px; #drawer-handle { padding: $spacing-vertical / 2; - max-height: $header-height - $spacing-vertical; + max-height: $height-header - $spacing-vertical; text-align: center; } @@ -76,10 +76,10 @@ $drawer-width: 240px; background: $color-primary; color: white; &.header-no-subnav { - height: $header-height; + height: $height-header; } &.header-with-subnav { - height: $header-height * 2; + height: $height-header * 2; } position: fixed; top: 0; @@ -87,7 +87,7 @@ $drawer-width: 240px; width: 100%; z-index: 2; box-sizing: border-box; - h1 { font-size: 1.8em; line-height: $header-height - $spacing-vertical; display: inline-block; float: left; } + h1 { font-size: 1.8em; line-height: $height-header - $spacing-vertical; display: inline-block; float: left; } &.header-scrolled { box-shadow: $default-box-shadow; @@ -120,7 +120,7 @@ nav.sub-header display: inline-block; margin: 0 15px; padding: 0 5px; - line-height: $header-height - $spacing-vertical - $sub-header-selected-underline-height; + line-height: $height-header - $spacing-vertical - $sub-header-selected-underline-height; color: #e8e8e8; &:first-child { @@ -147,13 +147,13 @@ nav.sub-header background: $color-canvas; &.no-sub-nav { - min-height: calc(100vh - 60px); //should be -$header-height, but I'm dumb I guess? It wouldn't work - main { margin-top: $header-height; } + min-height: calc(100vh - 60px); //should be -$height-header, but I'm dumb I guess? It wouldn't work + main { margin-top: $height-header; } } &.with-sub-nav { - min-height: calc(100vh - 120px); //should be -$header-height, but I'm dumb I guess? It wouldn't work - main { margin-top: $header-height * 2; } + min-height: calc(100vh - 120px); //should be -$height-header, but I'm dumb I guess? It wouldn't work + main { margin-top: $height-header * 2; } } main { @@ -206,9 +206,6 @@ $header-icon-size: 1.5em; box-shadow: $default-box-shadow; border-radius: 2px; } -.card-compact { - padding: 22px; -} .card-obscured { position: relative; diff --git a/scss/_global.scss b/scss/_global.scss index 50b6c44c8..b59b5821f 100644 --- a/scss/_global.scss +++ b/scss/_global.scss @@ -2,6 +2,8 @@ $spacing-vertical: 24px; +$padding-button: 12px; + $color-primary: #155B4A; $color-light-alt: hsl(hue($color-primary), 15, 85); $color-text-dark: #000; @@ -18,7 +20,8 @@ $mobile-width-threshold: 801px; $max-content-width: 1000px; $max-text-width: 660px; -$header-height: $spacing-vertical * 2.5; +$height-header: $spacing-vertical * 2.5; +$height-button: $spacing-vertical * 1.5; $default-box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); diff --git a/scss/_gui.scss b/scss/_gui.scss index cf5f72de0..137c60b24 100644 --- a/scss/_gui.scss +++ b/scss/_gui.scss @@ -1,6 +1,6 @@ @import "global"; -@mixin text-link($color: $color-primary, $hover-opacity: 0.70, $mirror: false) { +@mixin text-link($color: $color-primary, $hover-opacity: 0.70) { color: $color; .icon { @@ -28,18 +28,7 @@ } } - @if $mirror == false { - color: $color; - } - @else { - color: $color-bg; - background-color: $color; - position: absolute; - white-space: nowrap; - overflow: hidden; - top: 0px; - left: 0px; - } + color: $color; } .icon-fixed-width { @@ -156,16 +145,15 @@ input[type="text"], input[type="search"] + .button-container { - margin-left: 12px; + margin-left: $padding-button; } } -.button-block +.button-block, .faux-button-block { - cursor: pointer; display: inline-block; - height: $spacing-vertical * 1.5; - line-height: $spacing-vertical * 1.5; + height: $height-button; + line-height: $height-button; text-decoration: none; border: 0 none; text-align: center; @@ -184,37 +172,28 @@ input[type="text"], input[type="search"] padding-left: 5px; } } +.button-block +{ + cursor: pointer; +} .button-primary { color: white; background-color: $color-primary; box-shadow: $default-box-shadow; - padding: 0 12px; + padding: 0 $padding-button; } .button-alt { background-color: $color-bg-alt; box-shadow: $default-box-shadow; - padding: 0 12px; + padding: 0 $padding-button; } -.button-download -{ - padding: 0 6px; - - text-decoration: none !important; - - &.button-download--bg { - @include text-link(darken($color-primary, 1%)); - } - &.button-download--fg { - @include text-link(darken($color-primary, 1%), $mirror: true); - } -} .button-cancel { - padding: 0 12px; + padding: 0 $padding-button; } .button-text { @@ -378,11 +357,6 @@ input[type="text"], input[type="search"] background: rgba(#000, .88); } -.error-modal { - max-width: none; - width: 400px; -} - .error-modal__content { display: flex; padding: 0px 8px 10px 10px; @@ -397,56 +371,12 @@ input[type="text"], input[type="search"] word-break: break-all; } -.menu { - position: fixed; - white-space: nowrap; - background-color: $color-bg-alt; - box-shadow: $default-box-shadow; - padding: $spacing-vertical; - border-radius: 2px; + +.error-modal { + max-width: none; + width: 400px; } - -.menu__menu-item { - display: block; - text-decoration: none !important; - &:hover { - text-decoration: underline !important; - } -} - - -.file-tile--compact { - height: 180px; -} - -.file-tile__row { - height: 24px * 7; +.error-modal__error-list { /*shitty hack/temp fix for long errors making modals unusable*/ + max-height: 400px; overflow-y: hidden; } - -.file-tile__thumbnail { - max-width: 100%; - max-height: 24px * 7; - display: block; - margin-left: auto; - margin-right: auto; -} - -.file-tile__title { - font-weight: bold; -} - -.file-tile__title--compact { - font-size: 1.25em; - line-height: 1.15; -} - -.file-tile__cost { - float: right; -} - -.file-tile__description { - color: #444; - margin-top: 12px; - font-size: 0.9em; -} diff --git a/scss/all.scss b/scss/all.scss index e02fd6d3d..4055cc708 100644 --- a/scss/all.scss +++ b/scss/all.scss @@ -3,6 +3,9 @@ @import "_icons"; @import "_mediaelement"; @import "_canvas"; -@import "_table"; @import "_gui"; +@import "component/_table"; +@import "component/_file-actions.scss"; +@import "component/_file-tile.scss"; +@import "component/_menu.scss"; @import "page/_developer.scss"; \ No newline at end of file diff --git a/scss/component/_file-actions.scss b/scss/component/_file-actions.scss new file mode 100644 index 000000000..e17ed80d3 --- /dev/null +++ b/scss/component/_file-actions.scss @@ -0,0 +1,29 @@ +@import "../global"; + +$color-download: #444; + +.file-actions--stub +{ + height: $height-button; +} + +.file-actions__download-status-bar +{ + padding-right: $padding-button; + padding-left: $padding-button; + position: relative; + color: $color-download; +} +.file-actions__download-status-bar-overlay +{ + padding-right: $padding-button; + padding-left: $padding-button; + background: $color-download; + color: white; + position: absolute; + white-space: nowrap; + overflow: hidden; + z-index: 1; + top: 0px; + left: 0px; +} \ No newline at end of file diff --git a/scss/component/_file-tile.scss b/scss/component/_file-tile.scss new file mode 100644 index 000000000..11a071232 --- /dev/null +++ b/scss/component/_file-tile.scss @@ -0,0 +1,27 @@ +@import "../global"; + +.file-tile__row { + height: $spacing-vertical * 7; +} + +.file-tile__thumbnail { + max-width: 100%; + max-height: $spacing-vertical * 7; + display: block; + margin-left: auto; + margin-right: auto; +} + +.file-tile__title { + font-weight: bold; +} + +.file-tile__cost { + float: right; +} + +.file-tile__description { + color: #444; + margin-top: 12px; + font-size: 0.9em; +} \ No newline at end of file diff --git a/scss/component/_menu.scss b/scss/component/_menu.scss new file mode 100644 index 000000000..d46926bba --- /dev/null +++ b/scss/component/_menu.scss @@ -0,0 +1,21 @@ +@import "../global"; + +$border-radius-menu: 2px; + +.menu { + position: absolute; + white-space: nowrap; + background-color: white; + box-shadow: $default-box-shadow; + border-radius: $border-radius-menu; + padding-top: $spacing-vertical / 2; + padding-bottom: $spacing-vertical / 2; +} + +.menu__menu-item { + display: block; + padding: $spacing-vertical / 4 $spacing-vertical / 2; + &:hover { + background: $color-bg-alt; + } +} \ No newline at end of file diff --git a/scss/_table.scss b/scss/component/_table.scss similarity index 97% rename from scss/_table.scss rename to scss/component/_table.scss index 899010d60..9d60cf6e8 100644 --- a/scss/_table.scss +++ b/scss/component/_table.scss @@ -1,3 +1,5 @@ +@import "../global"; + table.table-standard { word-wrap: break-word; max-width: 100%;