diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf84a1a32..65aecac57 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,6 +55,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
* Handle more of price calculations at the daemon layer to improve page load time
* Add special support for building channel claims in lbryuri module
* Enable windows code signing of binary
+ * Support for opening LBRY URIs from links in other apps
### Changed
@@ -66,6 +67,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
### Fixed
* Fix Watch page and progress bars for new API changes
+ * On Windows, prevent opening multiple LBRY instances (launching LBRY again just focuses the current instance)
diff --git a/app/main.js b/app/main.js
index 90668b138..6fb0ba981 100644
--- a/app/main.js
+++ b/app/main.js
@@ -37,6 +37,22 @@ let readyToQuit = false;
// send it to, it's cached in this variable.
let openUri = null;
+function denormalizeUri(uri) {
+ // Windows normalizes URIs when they're passed in from other apps. This tries
+ // to restore the original URI that was typed.
+ // - If the URI has no path, Windows adds a trailing slash. LBRY URIs
+ // can't have a slash with no 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.
+
+ if (process.platform == 'win32') {
+ return uri.replace(/\/$/, '').replace('/#', '#');
+ } else {
+ return uri;
+ }
+}
+
function checkForNewVersion(callback) {
function formatRc(ver) {
// Adds dash if needed to make RC suffix semver friendly
@@ -186,6 +202,29 @@ function quitNow() {
app.quit();
}
+if (process.platform != 'linux') {
+ // On Linux, this is always returning true due to an Electron bug,
+ // so for now we just don't support single-instance apps on Linux.
+ const isSecondaryInstance = app.makeSingleInstance((argv) => {
+ if (win) {
+ if (win.isMinimized()) {
+ win.restore();
+ }
+ win.focus();
+
+ if (argv.length >= 2) {
+ win.webContents.send('open-uri-requested', denormalizeUri(argv[1]));
+ }
+ } else if (argv.length >= 2) {
+ openUri = denormalizeUri(argv[1]);
+ }
+ });
+
+ if (isSecondaryInstance) { // We're not in the original process, so quit
+ quitNow();
+ return;
+ }
+}
app.on('ready', function(){
launchDaemonIfNotRunning();
@@ -324,6 +363,8 @@ function upgrade(event, installerPath) {
ipcMain.on('upgrade', upgrade);
+app.setAsDefaultProtocolClient('lbry');
+
if (process.platform == 'darwin') {
app.on('open-url', (event, uri) => {
if (!win) {
@@ -333,7 +374,10 @@ if (process.platform == 'darwin') {
win.webContents.send('open-uri-requested', uri);
}
});
-} else if (process.argv.length >= 3) {
- // No open-url event on Win, but we can still handle URIs provided at launch time
- win.webContents.send('open-uri-requested', process.argv[2]);
+} else if (process.argv.length >= 2) {
+ if (!win) {
+ openUri = denormalizeUri(process.argv[1]);
+ } else {
+ win.webContents.send('open-uri-requested', denormalizeUri(process.argv[1]));
+ }
}
diff --git a/lbry b/lbry
new file mode 160000
index 000000000..d99fc519b
--- /dev/null
+++ b/lbry
@@ -0,0 +1 @@
+Subproject commit d99fc519b56ee910a44ef4af668b0770e9430d12
diff --git a/lbryschema b/lbryschema
new file mode 160000
index 000000000..5c2441fa1
--- /dev/null
+++ b/lbryschema
@@ -0,0 +1 @@
+Subproject commit 5c2441fa13e39ba7280292519041e14ec696d753
diff --git a/lbryum b/lbryum
new file mode 160000
index 000000000..950b95aa7
--- /dev/null
+++ b/lbryum
@@ -0,0 +1 @@
+Subproject commit 950b95aa7e45a2c15b269d807f6ff8e16bae4304
diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js
index 14bad12cd..8ede77fee 100644
--- a/ui/js/actions/app.js
+++ b/ui/js/actions/app.js
@@ -6,7 +6,6 @@ import {
selectUpgradeDownloadItem,
selectUpgradeFilename,
selectPageTitle,
- selectCurrentPath,
} from 'selectors/app'
const {remote, ipcRenderer, shell} = require('electron');
@@ -32,8 +31,7 @@ export function doNavigate(path, params = {}) {
const state = getState()
const pageTitle = selectPageTitle(state)
- history.pushState(params, pageTitle, url)
- window.document.title = pageTitle
+ dispatch(doHistoryPush(params, pageTitle, url))
}
}
@@ -45,7 +43,6 @@ export function doChangePath(path) {
path,
}
})
-
}
}
@@ -55,7 +52,15 @@ export function doHistoryBack() {
}
}
-export function doLogoClick() {
+export function doHistoryPush(params, title, relativeUrl) {
+ return function(dispatch, getState) {
+ let pathParts = window.location.pathname.split('/')
+ pathParts[pathParts.length - 1] = relativeUrl.replace(/^\//, '')
+ const url = pathParts.join('/')
+ title += " - LBRY"
+ history.pushState(params, title, url)
+ window.document.title = title
+ }
}
export function doOpenModal(modal) {
diff --git a/ui/js/actions/availability.js b/ui/js/actions/availability.js
index 74c85eaa3..501a2eeda 100644
--- a/ui/js/actions/availability.js
+++ b/ui/js/actions/availability.js
@@ -1,39 +1,29 @@
import * as types from 'constants/action_types'
import lbry from 'lbry'
import {
- selectCurrentUri,
-} from 'selectors/app'
+ selectFetchingAvailability
+} from 'selectors/availability'
-export function doFetchUriAvailability(uri) {
- return function(dispatch, getState) {
- dispatch({
- type: types.FETCH_AVAILABILITY_STARTED,
- data: { uri }
- })
-
- const successCallback = (availability) => {
- dispatch({
- type: types.FETCH_AVAILABILITY_COMPLETED,
- data: {
- availability,
- uri,
- }
- })
- }
-
- const errorCallback = () => {
- console.debug('error')
- }
-
- lbry.get_availability({ uri }, successCallback, errorCallback)
- }
-}
-
-export function doFetchCurrentUriAvailability() {
+export function doFetchAvailability(uri) {
return function(dispatch, getState) {
const state = getState()
- const uri = selectCurrentUri(state)
+ const alreadyFetching = !!selectFetchingAvailability(state)[uri]
- dispatch(doFetchUriAvailability(uri))
+ if (!alreadyFetching) {
+ dispatch({
+ type: types.FETCH_AVAILABILITY_STARTED,
+ data: {uri}
+ })
+
+ lbry.get_availability({uri}).then((availability) => {
+ dispatch({
+ type: types.FETCH_AVAILABILITY_COMPLETED,
+ data: {
+ availability,
+ uri,
+ }
+ })
+ })
+ }
}
-}
+}
\ No newline at end of file
diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js
index cc64d8d9a..40ff1b048 100644
--- a/ui/js/actions/content.js
+++ b/ui/js/actions/content.js
@@ -3,18 +3,18 @@ import lbry from 'lbry'
import lbryio from 'lbryio'
import lbryuri from 'lbryuri'
import rewards from 'rewards'
-import {
- selectCurrentUri,
-} from 'selectors/app'
import {
selectBalance,
} from 'selectors/wallet'
import {
- selectCurrentUriFileInfo,
- selectDownloadingByUri,
+ selectFileInfoForUri,
+ selectUrisDownloading,
} from 'selectors/file_info'
import {
- selectCurrentUriCostInfo,
+ selectResolvingUris
+} from 'selectors/content'
+import {
+ selectCostInfoForUri,
} from 'selectors/cost_info'
import {
selectClaimsByUri,
@@ -22,94 +22,44 @@ import {
import {
doOpenModal,
} from 'actions/app'
-import {
- doFetchCostInfoForUri,
-} from 'actions/cost_info'
export function doResolveUri(uri) {
return function(dispatch, getState) {
- dispatch({
- type: types.RESOLVE_URI_STARTED,
- data: { uri }
- })
- lbry.resolve({ uri }).then((resolutionInfo) => {
- const {
- claim,
- certificate,
- } = resolutionInfo ? resolutionInfo : { claim : null, certificate: null }
+ const state = getState()
+ const alreadyResolving = selectResolvingUris(state).indexOf(uri) !== -1
+ if (!alreadyResolving) {
dispatch({
- type: types.RESOLVE_URI_COMPLETED,
- data: {
- uri,
+ type: types.RESOLVE_URI_STARTED,
+ data: { uri }
+ })
+
+ lbry.resolve({ uri }).then((resolutionInfo) => {
+ const {
claim,
certificate,
- }
- })
-
- dispatch(doFetchCostInfoForUri(uri))
- })
- }
-}
-
-export function doFetchDownloadedContent() {
- return function(dispatch, getState) {
- const state = getState()
-
- dispatch({
- type: types.FETCH_DOWNLOADED_CONTENT_STARTED,
- })
-
- lbry.claim_list_mine().then((myClaimInfos) => {
- lbry.file_list().then((fileInfos) => {
- const myClaimOutpoints = myClaimInfos.map(({txid, nout}) => txid + ':' + nout);
-
- fileInfos.forEach(fileInfo => {
- const uri = lbryuri.build({
- channelName: fileInfo.channel_name,
- contentName: fileInfo.name,
- })
- const claim = selectClaimsByUri(state)[uri]
- if (!claim) dispatch(doResolveUri(uri))
- })
+ } = resolutionInfo ? resolutionInfo : { claim : null, certificate: null }
dispatch({
- type: types.FETCH_DOWNLOADED_CONTENT_COMPLETED,
+ type: types.RESOLVE_URI_COMPLETED,
data: {
- fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)),
- }
- })
- });
- });
- }
-}
-
-export function doFetchPublishedContent() {
- return function(dispatch, getState) {
- const state = getState()
-
- dispatch({
- type: types.FETCH_PUBLISHED_CONTENT_STARTED,
- })
-
- lbry.claim_list_mine().then((claimInfos) => {
- dispatch({
- type: types.FETCH_MY_CLAIMS_COMPLETED,
- data: {
- claims: claimInfos,
- }
- })
- lbry.file_list().then((fileInfos) => {
- const myClaimOutpoints = claimInfos.map(({txid, nout}) => txid + ':' + nout)
-
- dispatch({
- type: types.FETCH_PUBLISHED_CONTENT_COMPLETED,
- data: {
- fileInfos: fileInfos.filter(({outpoint}) => myClaimOutpoints.includes(outpoint)),
+ uri,
+ claim,
+ certificate,
}
})
})
+ }
+ }
+}
+
+export function doCancelResolveUri(uri) {
+ return function(dispatch, getState) {
+ lbry.cancelResolve({ uri })
+ dispatch({
+ type: types.RESOLVE_URI_CANCELED,
+ data: { uri }
})
}
}
@@ -131,6 +81,14 @@ export function doFetchFeaturedUris() {
featuredUris[category] = Uris[category]
}
})
+ //
+ // dispatch({
+ // type: types.FETCH_FEATURED_CONTENT_COMPLETED,
+ // data: {
+ // categories: ["FOO"],
+ // uris: { FOO: ["lbry://gtasoc"]},
+ // }
+ // })
dispatch({
type: types.FETCH_FEATURED_CONTENT_COMPLETED,
@@ -175,6 +133,7 @@ export function doUpdateLoadStatus(uri, outpoint) {
type: types.DOWNLOADING_COMPLETED,
data: {
uri,
+ outpoint,
fileInfo,
}
})
@@ -190,6 +149,7 @@ export function doUpdateLoadStatus(uri, outpoint) {
type: types.DOWNLOADING_PROGRESSED,
data: {
uri,
+ outpoint,
fileInfo,
progress,
}
@@ -200,13 +160,6 @@ export function doUpdateLoadStatus(uri, outpoint) {
}
}
-export function doPlayVideo(uri) {
- return {
- type: types.PLAY_VIDEO_STARTED,
- data: { uri }
- }
-}
-
export function doDownloadFile(uri, streamInfo) {
return function(dispatch, getState) {
const state = getState()
@@ -216,6 +169,7 @@ export function doDownloadFile(uri, streamInfo) {
type: types.DOWNLOADING_STARTED,
data: {
uri,
+ outpoint: streamInfo.outpoint,
fileInfo,
}
})
@@ -230,10 +184,9 @@ export function doDownloadFile(uri, streamInfo) {
}
}
-export function doLoadVideo() {
+export function doLoadVideo(uri) {
return function(dispatch, getState) {
const state = getState()
- const uri = selectCurrentUri(state)
dispatch({
type: types.LOADING_VIDEO_STARTED,
@@ -260,14 +213,13 @@ export function doLoadVideo() {
}
}
-export function doWatchVideo() {
+export function doPurchaseUri(uri) {
return function(dispatch, getState) {
const state = getState()
- const uri = selectCurrentUri(state)
const balance = selectBalance(state)
- const fileInfo = selectCurrentUriFileInfo(state)
- const costInfo = selectCurrentUriCostInfo(state)
- const downloadingByUri = selectDownloadingByUri(state)
+ const fileInfo = selectFileInfoForUri(state, { uri })
+ const costInfo = selectCostInfoForUri(state, { uri })
+ const downloadingByUri = selectUrisDownloading(state)
const alreadyDownloading = !!downloadingByUri[uri]
const { cost } = costInfo
@@ -288,8 +240,8 @@ export function doWatchVideo() {
}
// the file is free or we have partially downloaded it
- if (cost <= 0.01 || fileInfo.download_directory) {
- dispatch(doLoadVideo())
+ if (cost <= 0.01 || (fileInfo && fileInfo.download_directory)) {
+ dispatch(doLoadVideo(uri))
return Promise.resolve()
}
@@ -302,3 +254,44 @@ export function doWatchVideo() {
return Promise.resolve()
}
}
+
+export function doFetchClaimsByChannel(uri) {
+ return function(dispatch, getState) {
+ dispatch({
+ type: types.FETCH_CHANNEL_CLAIMS_STARTED,
+ data: { uri }
+ })
+
+ lbry.resolve({ uri }).then((resolutionInfo) => {
+ const {
+ claims_in_channel,
+ } = resolutionInfo ? resolutionInfo : { claims_in_channel: [] }
+
+ dispatch({
+ type: types.FETCH_CHANNEL_CLAIMS_COMPLETED,
+ data: {
+ uri,
+ claims: claims_in_channel
+ }
+ })
+ })
+ }
+}
+
+export function doClaimListMine() {
+ return function(dispatch, getState) {
+ dispatch({
+ type: types.CLAIM_LIST_MINE_STARTED
+ })
+
+
+ lbry.claim_list_mine().then((claims) => {
+ dispatch({
+ type: types.CLAIM_LIST_MINE_COMPLETED,
+ data: {
+ claims
+ }
+ })
+ })
+ }
+}
\ No newline at end of file
diff --git a/ui/js/actions/cost_info.js b/ui/js/actions/cost_info.js
index 5d28fb42c..79ff4bdd5 100644
--- a/ui/js/actions/cost_info.js
+++ b/ui/js/actions/cost_info.js
@@ -1,19 +1,51 @@
import * as types from 'constants/action_types'
-import {
- selectCurrentUri,
-} from 'selectors/app'
import lbry from 'lbry'
+import lbryio from 'lbryio'
+import {
+ doResolveUri
+} from 'actions/content'
+import {
+ selectResolvingUris,
+} from 'selectors/content'
+import {
+ selectClaimsByUri
+} from 'selectors/claims'
+import {
+ selectSettingsIsGenerous
+} from 'selectors/settings'
export function doFetchCostInfoForUri(uri) {
return function(dispatch, getState) {
- dispatch({
- type: types.FETCH_COST_INFO_STARTED,
- data: {
- uri,
- }
- })
+ const state = getState(),
+ claim = selectClaimsByUri(state)[uri],
+ isResolving = selectResolvingUris(state).indexOf(uri) !== -1,
+ isGenerous = selectSettingsIsGenerous(state)
- lbry.getCostInfo(uri).then(costInfo => {
+ if (claim === null) { //claim doesn't exist, nothing to fetch a cost for
+ return
+ }
+
+ if (!claim) {
+ setTimeout(() => {
+ dispatch(doFetchCostInfoForUri(uri))
+ }, 1000)
+ if (!isResolving) {
+ dispatch(doResolveUri(uri))
+ }
+ return
+ }
+
+
+ function begin() {
+ dispatch({
+ type: types.FETCH_COST_INFO_STARTED,
+ data: {
+ uri,
+ }
+ })
+ }
+
+ function resolve(costInfo) {
dispatch({
type: types.FETCH_COST_INFO_COMPLETED,
data: {
@@ -21,15 +53,25 @@ export function doFetchCostInfoForUri(uri) {
costInfo,
}
})
- }).catch(() => {
- dispatch({
- type: types.FETCH_COST_INFO_COMPLETED,
- data: {
- uri,
- costInfo: {}
- }
- })
- })
+ }
+
+ if (isGenerous && claim) {
+ let cost
+ const fee = claim.value.stream.metadata.fee;
+ if (fee === undefined ) {
+ resolve({ cost: 0, includesData: true })
+ } else if (fee.currency == 'LBC') {
+ resolve({ cost: fee.amount, includesData: true })
+ } else {
+ begin()
+ lbryio.getExchangeRates().then(({lbc_usd}) => {
+ resolve({ cost: fee.amount / lbc_usd, includesData: true })
+ });
+ }
+ } else {
+ begin()
+ lbry.getCostInfo(uri).then(resolve)
+ }
}
}
diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js
index b7611fb3d..23ba50e71 100644
--- a/ui/js/actions/file_info.js
+++ b/ui/js/actions/file_info.js
@@ -1,11 +1,17 @@
import * as types from 'constants/action_types'
import lbry from 'lbry'
import {
- selectCurrentUri,
-} from 'selectors/app'
+ doClaimListMine
+} from 'actions/content'
import {
- selectCurrentUriClaimOutpoint,
+ selectClaimsByUri,
+ selectClaimListMineIsPending,
} from 'selectors/claims'
+import {
+ selectFileListIsPending,
+ selectAllFileInfos,
+ selectUrisLoading,
+} from 'selectors/file_info'
import {
doCloseModal,
} from 'actions/app'
@@ -14,29 +20,54 @@ const {
shell,
} = require('electron')
-export function doFetchCurrentUriFileInfo() {
+export function doFetchFileInfo(uri) {
return function(dispatch, getState) {
const state = getState()
- const uri = selectCurrentUri(state)
- const outpoint = selectCurrentUriClaimOutpoint(state)
+ const claim = selectClaimsByUri(state)[uri]
+ const outpoint = claim ? `${claim.txid}:${claim.nout}` : null
+ const alreadyFetching = !!selectUrisLoading(state)[uri]
- dispatch({
- type: types.FETCH_FILE_INFO_STARTED,
- data: {
- uri,
- outpoint,
- }
- })
-
- lbry.file_list({ outpoint: outpoint, full_status: true }).then(([fileInfo]) => {
+ if (!alreadyFetching) {
dispatch({
- type: types.FETCH_FILE_INFO_COMPLETED,
+ type: types.FETCH_FILE_INFO_STARTED,
data: {
- uri,
- fileInfo,
+ outpoint,
}
})
- })
+
+ lbry.file_list({outpoint: outpoint, full_status: true}).then(fileInfos => {
+
+ dispatch({
+ type: types.FETCH_FILE_INFO_COMPLETED,
+ data: {
+ outpoint,
+ fileInfo: fileInfos && fileInfos.length ? fileInfos[0] : null,
+ }
+ })
+ })
+ }
+ }
+}
+
+export function doFileList() {
+ return function(dispatch, getState) {
+ const state = getState()
+ const isPending = selectFileListIsPending(state)
+
+ if (!isPending) {
+ dispatch({
+ type: types.FILE_LIST_STARTED,
+ })
+
+ lbry.file_list().then((fileInfos) => {
+ dispatch({
+ type: types.FILE_LIST_COMPLETED,
+ data: {
+ fileInfos,
+ }
+ })
+ })
+ }
}
}
@@ -52,51 +83,39 @@ export function doOpenFileInFolder(fileInfo) {
}
}
-export function doDeleteFile(uri, fileInfo, deleteFromComputer) {
+export function doDeleteFile(outpoint, deleteFromComputer) {
return function(dispatch, getState) {
+
dispatch({
- type: types.DELETE_FILE_STARTED,
+ type: types.FILE_DELETE,
data: {
- uri,
- fileInfo,
- deleteFromComputer,
+ outpoint
}
})
- const successCallback = () => {
- dispatch({
- type: types.DELETE_FILE_COMPLETED,
- data: {
- uri,
- }
- })
- dispatch(doCloseModal())
- }
-
- lbry.removeFile(fileInfo.outpoint, deleteFromComputer, successCallback)
- }
-}
-
-export function doFetchDownloadedContent() {
- return function(dispatch, getState) {
- const state = getState()
-
- dispatch({
- type: types.FETCH_DOWNLOADED_CONTENT_STARTED,
+ lbry.file_delete({
+ outpoint: outpoint,
+ delete_target_file: deleteFromComputer,
})
- lbry.claim_list_mine().then((myClaimInfos) => {
- lbry.file_list().then((fileInfos) => {
- const myClaimOutpoints = myClaimInfos.map(({txid, nout}) => txid + ':' + nout);
-
- dispatch({
- type: types.FETCH_DOWNLOADED_CONTENT_COMPLETED,
- data: {
- fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)),
- }
- })
- });
- });
+ dispatch(doCloseModal())
+ }
+}
+
+
+export function doFetchFileInfosAndPublishedClaims() {
+ return function(dispatch, getState) {
+ const state = getState(),
+ isClaimListMinePending = selectClaimListMineIsPending(state),
+ isFileInfoListPending = selectFileListIsPending(state)
+
+ if (isClaimListMinePending === undefined) {
+ dispatch(doClaimListMine())
+ }
+
+ if (isFileInfoListPending === undefined) {
+ dispatch(doFileList())
+ }
}
}
diff --git a/ui/js/actions/search.js b/ui/js/actions/search.js
index b2748ff1b..8dd85d822 100644
--- a/ui/js/actions/search.js
+++ b/ui/js/actions/search.js
@@ -6,6 +6,7 @@ import {
} from 'actions/content'
import {
doNavigate,
+ doHistoryPush
} from 'actions/app'
import {
selectCurrentPage,
@@ -29,6 +30,8 @@ export function doSearch(query) {
if(page != 'search') {
dispatch(doNavigate('search', { query: query }))
+ } else {
+ dispatch(doHistoryPush({ query }, "Search for " + query, '/search'))
}
lighthouse.search(query).then(results => {
diff --git a/ui/js/actions/settings.js b/ui/js/actions/settings.js
new file mode 100644
index 000000000..18e3a5004
--- /dev/null
+++ b/ui/js/actions/settings.js
@@ -0,0 +1,31 @@
+import * as types from 'constants/action_types'
+import lbry from 'lbry'
+
+export function doFetchDaemonSettings() {
+ return function(dispatch, getState) {
+ lbry.get_settings().then((settings) => {
+ dispatch({
+ type: types.DAEMON_SETTINGS_RECEIVED,
+ data: {
+ settings
+ }
+ })
+ })
+ }
+}
+
+export function doSetDaemonSetting(key, value) {
+ return function(dispatch, getState) {
+ let settings = {};
+ settings[key] = value;
+ lbry.settings_set(settings).then(settings)
+ lbry.get_settings().then((settings) => {
+ dispatch({
+ type: types.DAEMON_SETTINGS_RECEIVED,
+ data: {
+ settings
+ }
+ })
+ })
+ }
+}
\ No newline at end of file
diff --git a/ui/js/component/app/index.js b/ui/js/component/app/index.js
index a94c0e6f6..dcb94bd0f 100644
--- a/ui/js/component/app/index.js
+++ b/ui/js/component/app/index.js
@@ -2,30 +2,23 @@ import React from 'react';
import { connect } from 'react-redux'
import {
- selectCurrentPage,
selectCurrentModal,
- selectDrawerOpen,
- selectHeaderLinks,
- selectSearchTerm,
} from 'selectors/app'
import {
doCheckUpgradeAvailable,
- doOpenModal,
- doCloseModal,
} from 'actions/app'
+import {
+ doUpdateBalance,
+} from 'actions/wallet'
import App from './view'
const select = (state) => ({
- currentPage: selectCurrentPage(state),
modal: selectCurrentModal(state),
- headerLinks: selectHeaderLinks(state),
- searchTerm: selectSearchTerm(state)
})
const perform = (dispatch) => ({
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
- openModal: () => dispatch(doOpenModal()),
- closeModal: () => dispatch(doCloseModal()),
+ updateBalance: (balance) => dispatch(doUpdateBalance(balance))
})
export default connect(select, perform)(App)
diff --git a/ui/js/component/app/view.jsx b/ui/js/component/app/view.jsx
index e6162c4dd..1c7ff4eb4 100644
--- a/ui/js/component/app/view.jsx
+++ b/ui/js/component/app/view.jsx
@@ -4,7 +4,8 @@ import Header from 'component/header';
import ErrorModal from 'component/errorModal'
import DownloadingModal from 'component/downloadingModal'
import UpgradeModal from 'component/upgradeModal'
-import {Line} from 'rc-progress';
+import lbry from 'lbry'
+import {Line} from 'rc-progress'
class App extends React.Component {
componentWillMount() {
@@ -15,12 +16,15 @@ class App extends React.Component {
if (!this.props.upgradeSkipped) {
this.props.checkUpgradeAvailable()
}
+
+ lbry.balanceSubscribe((balance) => {
+ this.props.updateBalance(balance)
+ })
}
render() {
const {
modal,
- headerLinks,
} = this.props
return
diff --git a/ui/js/component/auth.js b/ui/js/component/auth.js
index 261b065b1..7b934f339 100644
--- a/ui/js/component/auth.js
+++ b/ui/js/component/auth.js
@@ -200,7 +200,7 @@ const CodeRequiredStage = React.createClass({
})
if (!this.state.address) {
- lbry.getUnusedAddress((address) => {
+ lbry.wallet_unused_address().then((address) => {
setLocal('wallet_address', address);
this.setState({ address: address });
});
diff --git a/ui/js/component/fileActions/index.js b/ui/js/component/fileActions/index.js
index 21a2c05d0..a570cd413 100644
--- a/ui/js/component/fileActions/index.js
+++ b/ui/js/component/fileActions/index.js
@@ -3,9 +3,6 @@ import {
connect,
} from 'react-redux'
import {
- selectObscureNsfw,
- selectHidePrice,
- selectHasSignature,
selectPlatform,
} from 'selectors/app'
import {
@@ -14,7 +11,7 @@ import {
makeSelectLoadingForUri,
} from 'selectors/file_info'
import {
- makeSelectAvailabilityForUri,
+ makeSelectIsAvailableForUri,
} from 'selectors/availability'
import {
selectCurrentModal,
@@ -22,47 +19,50 @@ import {
import {
doCloseModal,
doOpenModal,
+ doHistoryBack,
} from 'actions/app'
+import {
+ doFetchAvailability
+} from 'actions/availability'
import {
doOpenFileInShell,
doOpenFileInFolder,
doDeleteFile,
} from 'actions/file_info'
import {
- doWatchVideo,
+ doPurchaseUri,
doLoadVideo,
} from 'actions/content'
import FileActions from './view'
const makeSelect = () => {
const selectFileInfoForUri = makeSelectFileInfoForUri()
- const selectAvailabilityForUri = makeSelectAvailabilityForUri()
+ const selectIsAvailableForUri = makeSelectIsAvailableForUri()
const selectDownloadingForUri = makeSelectDownloadingForUri()
- const selectLoadingForUri = makeSelectLoadingForUri()
const select = (state, props) => ({
- obscureNsfw: selectObscureNsfw(state),
- hidePrice: selectHidePrice(state),
- hasSignature: selectHasSignature(state),
fileInfo: selectFileInfoForUri(state, props),
- availability: selectAvailabilityForUri(state, props),
+ isAvailable: selectIsAvailableForUri(state, props),
platform: selectPlatform(state),
modal: selectCurrentModal(state),
downloading: selectDownloadingForUri(state, props),
- loading: selectLoadingForUri(state, props),
})
return select
}
const perform = (dispatch) => ({
+ checkAvailability: (uri) => dispatch(doFetchAvailability(uri)),
closeModal: () => dispatch(doCloseModal()),
openInFolder: (fileInfo) => dispatch(doOpenFileInFolder(fileInfo)),
openInShell: (fileInfo) => dispatch(doOpenFileInShell(fileInfo)),
- deleteFile: (fileInfo, deleteFromComputer) => dispatch(doDeleteFile(fileInfo, deleteFromComputer)),
+ deleteFile: (fileInfo, deleteFromComputer) => {
+ dispatch(doHistoryBack())
+ dispatch(doDeleteFile(fileInfo, deleteFromComputer))
+ },
openModal: (modal) => dispatch(doOpenModal(modal)),
- downloadClick: () => dispatch(doWatchVideo()),
- loadVideo: () => dispatch(doLoadVideo())
+ startDownload: (uri) => dispatch(doPurchaseUri(uri)),
+ loadVideo: (uri) => dispatch(doLoadVideo(uri))
})
-export default connect(makeSelect, perform)(FileActions)
+export default connect(makeSelect, perform)(FileActions)
\ No newline at end of file
diff --git a/ui/js/component/fileActions/view.jsx b/ui/js/component/fileActions/view.jsx
index 5d74949bc..c1e9d6e3c 100644
--- a/ui/js/component/fileActions/view.jsx
+++ b/ui/js/component/fileActions/view.jsx
@@ -1,7 +1,5 @@
import React from 'react';
-import lbry from 'lbry';
-import lbryuri from 'lbryuri';
-import {Icon,} from 'component/common';
+import {Icon,BusyMessage} from 'component/common';
import FilePrice from 'component/filePrice'
import {Modal} from 'component/modal';
import {FormField} from 'component/form';
@@ -9,14 +7,36 @@ import Link from 'component/link';
import {ToolTip} from 'component/tooltip';
import {DropDownMenu, DropDownMenuItem} from 'component/menu';
-class FileActionsRow extends React.Component {
+class FileActions extends React.Component {
constructor(props) {
super(props)
this.state = {
+ forceShowActions: false,
deleteChecked: false,
}
}
+ componentWillMount() {
+ this.checkAvailability(this.props.uri)
+ }
+
+ componentWillReceiveProps(nextProps) {
+ this.checkAvailability(nextProps.uri)
+ }
+
+ checkAvailability(uri) {
+ if (!this._uri || uri !== this._uri) {
+ this._uri = uri;
+ this.props.checkAvailability(uri)
+ }
+ }
+
+ onShowFileActionsRowClicked() {
+ this.setState({
+ forceShowActions: true,
+ });
+ }
+
handleDeleteCheckboxClicked(event) {
this.setState({
deleteChecked: event.target.checked,
@@ -25,65 +45,73 @@ class FileActionsRow extends React.Component {
onAffirmPurchase() {
this.props.closeModal()
- this.props.loadVideo()
+ this.props.loadVideo(this.props.uri)
}
render() {
const {
fileInfo,
+ isAvailable,
platform,
downloading,
- loading,
uri,
deleteFile,
openInFolder,
openInShell,
modal,
openModal,
- affirmPurchase,
closeModal,
- downloadClick,
+ startDownload,
} = this.props
- const {
- deleteChecked,
- } = this.state
+ const deleteChecked = this.state.deleteChecked,
+ metadata = fileInfo ? fileInfo.metadata : null,
+ openInFolderMessage = platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder',
+ showMenu = fileInfo && Object.keys(fileInfo).length > 0,
+ title = metadata ? metadata.title : uri;
- const metadata = fileInfo ? fileInfo.metadata : null
+ let content
- if (!fileInfo)
- {
- return null;
- }
+ if (downloading) {
- const openInFolderMessage = platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder',
- showMenu = Object.keys(fileInfo).length != 0;
-
- let linkBlock;
- if (Object.keys(fileInfo).length == 0 && !downloading && !loading) {
- linkBlock =
;
- } else if (downloading || loading) {
const
progress = (fileInfo && fileInfo.written_bytes) ? fileInfo.written_bytes / fileInfo.total_bytes * 100 : 0,
label = fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...',
labelWithIcon =
{label};
- linkBlock = (
-
-
{labelWithIcon}
- {labelWithIcon}
-
- );
+ content =
+
{labelWithIcon}
+ {labelWithIcon}
+
+
+ } else if (!fileInfo && isAvailable === undefined) {
+
+ content =
+
+ } else if (!fileInfo && !isAvailable && !this.state.forceShowActions) {
+
+ content =
+
Content unavailable.
+
+
+
+
+ } else if (fileInfo === null && !downloading) {
+
+ content =
{ startDownload(uri) } } />;
+
+ } else if (fileInfo && fileInfo.download_path) {
+ content =
openInShell(fileInfo)} />;
} else {
- linkBlock =
openInShell(fileInfo)} />;
+ console.log('handle this case of file action props?');
+ console.log(this.props)
}
- const title = metadata ? metadata.title : uri;
return (
-
- {fileInfo !== null || fileInfo.isMine
- ? linkBlock
- : null}
+
+ { content }
{ showMenu ?
openInFolder(fileInfo)} label={openInFolderMessage} />
@@ -91,7 +119,7 @@ class FileActionsRow extends React.Component {
: '' }
- Are you sure you'd like to buy {title} for credits?
+ This will purchase {title} for credits.
@@ -102,62 +130,18 @@ class FileActionsRow extends React.Component {
LBRY was unable to download the stream {uri}.
deleteFile(uri, fileInfo, deleteChecked)}
- onAborted={closeModal}>
+ contentLabel="Not enough credits"
+ type="confirm"
+ confirmButtonLabel="Remove"
+ onConfirmed={() => deleteFile(fileInfo.outpoint, deleteChecked)}
+ onAborted={closeModal}>
Are you sure you'd like to remove {title} from LBRY?
-
+
);
}
}
-class FileActions extends React.Component {
- constructor(props) {
- super(props)
- this._isMounted = false
- this._fileInfoSubscribeId = null
- this.state = {
- available: true,
- forceShowActions: false,
- fileInfo: null,
- }
- }
-
- onShowFileActionsRowClicked() {
- this.setState({
- forceShowActions: true,
- });
- }
-
- render() {
- const {
- fileInfo,
- availability,
- } = this.props
-
- if (fileInfo === null) {
- return null;
- }
-
- return (
- {
- fileInfo || this.state.available || this.state.forceShowActions
- ?
- :
-
Content unavailable.
-
-
-
- }
- );
- }
-}
-
export default FileActions
diff --git a/ui/js/component/fileCard/index.js b/ui/js/component/fileCard/index.js
index 1fcd3ab9b..89ebc5040 100644
--- a/ui/js/component/fileCard/index.js
+++ b/ui/js/component/fileCard/index.js
@@ -7,21 +7,20 @@ import {
} from 'actions/app'
import {
doResolveUri,
+ doCancelResolveUri,
} from 'actions/content'
import {
- selectHidePrice,
selectObscureNsfw,
} from 'selectors/app'
import {
makeSelectClaimForUri,
- makeSelectSourceForUri,
makeSelectMetadataForUri,
} from 'selectors/claims'
import {
makeSelectFileInfoForUri,
} from 'selectors/file_info'
import {
- makeSelectResolvingUri,
+ makeSelectIsResolvingForUri,
} from 'selectors/content'
import FileCard from './view'
@@ -29,17 +28,13 @@ const makeSelect = () => {
const selectClaimForUri = makeSelectClaimForUri()
const selectFileInfoForUri = makeSelectFileInfoForUri()
const selectMetadataForUri = makeSelectMetadataForUri()
- const selectSourceForUri = makeSelectSourceForUri()
- const selectResolvingUri = makeSelectResolvingUri()
+ const selectResolvingUri = makeSelectIsResolvingForUri()
const select = (state, props) => ({
claim: selectClaimForUri(state, props),
fileInfo: selectFileInfoForUri(state, props),
- hidePrice: selectHidePrice(state),
obscureNsfw: selectObscureNsfw(state),
- hasSignature: false,
metadata: selectMetadataForUri(state, props),
- source: selectSourceForUri(state, props),
isResolvingUri: selectResolvingUri(state, props),
})
@@ -49,6 +44,7 @@ const makeSelect = () => {
const perform = (dispatch) => ({
navigate: (path, params) => dispatch(doNavigate(path, params)),
resolveUri: (uri) => dispatch(doResolveUri(uri)),
+ cancelResolveUri: (uri) => dispatch(doCancelResolveUri(uri))
})
export default connect(makeSelect, perform)(FileCard)
diff --git a/ui/js/component/fileCard/view.jsx b/ui/js/component/fileCard/view.jsx
index 19e03bc1f..918476a53 100644
--- a/ui/js/component/fileCard/view.jsx
+++ b/ui/js/component/fileCard/view.jsx
@@ -2,21 +2,41 @@ import React from 'react';
import lbry from 'lbry.js';
import lbryuri from 'lbryuri.js';
import Link from 'component/link';
-import {Thumbnail, TruncatedText,} from 'component/common';
+import {Thumbnail, TruncatedText, Icon} from 'component/common';
import FilePrice from 'component/filePrice'
import UriIndicator from 'component/uriIndicator';
class FileCard extends React.Component {
- componentDidMount() {
+ componentWillMount() {
+ this.resolve(this.props)
+ }
+
+ componentWillReceiveProps(nextProps) {
+ this.resolve(nextProps)
+ }
+
+ resolve(props) {
const {
isResolvingUri,
resolveUri,
claim,
uri,
+ } = props
+
+ if(!isResolvingUri && claim === undefined && uri) {
+ resolveUri(uri)
+ }
+ }
+
+ componentWillUnmount() {
+ const {
+ isResolvingUri,
+ cancelResolveUri,
+ uri
} = this.props
- if(!isResolvingUri && !claim && uri) {
- resolveUri(uri)
+ if (isResolvingUri) {
+ cancelResolveUri(uri)
}
}
@@ -35,10 +55,11 @@ class FileCard extends React.Component {
render() {
const {
+ claim,
+ fileInfo,
metadata,
isResolvingUri,
navigate,
- hidePrice,
} = this.props
const uri = lbryuri.normalize(this.props.uri);
@@ -50,6 +71,8 @@ class FileCard extends React.Component {
description = "Loading..."
} else if (metadata && metadata.description) {
description = metadata.description
+ } else if (claim === null) {
+ description = 'This address contains no content.'
}
return (
@@ -59,7 +82,10 @@ class FileCard extends React.Component {
{title}
- { !hidePrice ? : null}
+
+
+ { fileInfo ? {' '} : '' }
+
diff --git a/ui/js/component/fileList/view.jsx b/ui/js/component/fileList/view.jsx
index ebd94c0ee..eb3b620b8 100644
--- a/ui/js/component/fileList/view.jsx
+++ b/ui/js/component/fileList/view.jsx
@@ -58,8 +58,8 @@ class FileList extends React.Component {
render() {
const {
handleSortChanged,
+ fetching,
fileInfos,
- hidePrices,
} = this.props
const {
sortBy,
@@ -71,10 +71,11 @@ class FileList extends React.Component {
contentName: fileInfo.name,
channelName: fileInfo.channel_name,
})
- content.push(
)
+ content.push(
)
})
return (
-
+
+ { fetching && }
Sort by { ' ' }
diff --git a/ui/js/component/filePrice/index.js b/ui/js/component/filePrice/index.js
index c5a9497d0..726ceb3ee 100644
--- a/ui/js/component/filePrice/index.js
+++ b/ui/js/component/filePrice/index.js
@@ -2,6 +2,9 @@ import React from 'react'
import {
connect,
} from 'react-redux'
+import {
+ doFetchCostInfoForUri,
+} from 'actions/cost_info'
import {
makeSelectCostInfoForUri,
} from 'selectors/cost_info'
@@ -9,6 +12,7 @@ import FilePrice from './view'
const makeSelect = () => {
const selectCostInfoForUri = makeSelectCostInfoForUri()
+
const select = (state, props) => ({
costInfo: selectCostInfoForUri(state, props),
})
@@ -17,6 +21,8 @@ const makeSelect = () => {
}
const perform = (dispatch) => ({
+ fetchCostInfo: (uri) => dispatch(doFetchCostInfoForUri(uri)),
+ // cancelFetchCostInfo: (uri) => dispatch(doCancelFetchCostInfoForUri(uri))
})
export default connect(makeSelect, perform)(FilePrice)
diff --git a/ui/js/component/filePrice/view.jsx b/ui/js/component/filePrice/view.jsx
index 17b830bf2..4edc3a3e7 100644
--- a/ui/js/component/filePrice/view.jsx
+++ b/ui/js/component/filePrice/view.jsx
@@ -3,19 +3,41 @@ import {
CreditAmount,
} from 'component/common'
-const FilePrice = (props) => {
- const {
- costInfo,
- look = 'indicator',
- } = props
-
- const isEstimate = costInfo ? !costInfo.includesData : null
-
- if (!costInfo) {
- return ???;
+class FilePrice extends React.Component{
+ componentWillMount() {
+ this.fetchCost(this.props)
}
- return
+ componentWillReceiveProps(nextProps) {
+ this.fetchCost(nextProps)
+ }
+
+ fetchCost(props) {
+ const {
+ costInfo,
+ fetchCostInfo,
+ uri
+ } = props
+
+ if (costInfo === undefined) {
+ fetchCostInfo(uri)
+ }
+ }
+
+ render() {
+ const {
+ costInfo,
+ look = 'indicator',
+ } = this.props
+
+ const isEstimate = costInfo ? !costInfo.includesData : null
+
+ if (!costInfo) {
+ return ???;
+ }
+
+ return
+ }
}
export default FilePrice
diff --git a/ui/js/component/fileTile/index.js b/ui/js/component/fileTile/index.js
index dd17e8e60..d61e58b64 100644
--- a/ui/js/component/fileTile/index.js
+++ b/ui/js/component/fileTile/index.js
@@ -10,41 +10,30 @@ import {
} from 'actions/content'
import {
makeSelectClaimForUri,
- makeSelectSourceForUri,
makeSelectMetadataForUri,
} from 'selectors/claims'
import {
makeSelectFileInfoForUri,
} from 'selectors/file_info'
-import {
- makeSelectFetchingAvailabilityForUri,
- makeSelectAvailabilityForUri,
-} from 'selectors/availability'
import {
selectObscureNsfw,
} from 'selectors/app'
import {
- makeSelectResolvingUri,
+ makeSelectIsResolvingForUri,
} from 'selectors/content'
import FileTile from './view'
const makeSelect = () => {
const selectClaimForUri = makeSelectClaimForUri()
const selectFileInfoForUri = makeSelectFileInfoForUri()
- const selectFetchingAvailabilityForUri = makeSelectFetchingAvailabilityForUri()
- const selectAvailabilityForUri = makeSelectAvailabilityForUri()
const selectMetadataForUri = makeSelectMetadataForUri()
- const selectSourceForUri = makeSelectSourceForUri()
- const selectResolvingUri = makeSelectResolvingUri()
+ const selectResolvingUri = makeSelectIsResolvingForUri()
const select = (state, props) => ({
claim: selectClaimForUri(state, props),
fileInfo: selectFileInfoForUri(state, props),
- fetchingAvailability: selectFetchingAvailabilityForUri(state, props),
- selectAvailabilityForUri: selectAvailabilityForUri(state, props),
obscureNsfw: selectObscureNsfw(state),
metadata: selectMetadataForUri(state, props),
- source: selectSourceForUri(state, props),
isResolvingUri: selectResolvingUri(state, props),
})
diff --git a/ui/js/component/fileTile/view.jsx b/ui/js/component/fileTile/view.jsx
index 455e59d5a..250bf6296 100644
--- a/ui/js/component/fileTile/view.jsx
+++ b/ui/js/component/fileTile/view.jsx
@@ -13,11 +13,8 @@ class FileTile extends React.Component {
constructor(props) {
super(props)
- this._fileInfoSubscribeId = null
- this._isMounted = null
this.state = {
showNsfwHelp: false,
- isHidden: false,
}
}
@@ -29,31 +26,11 @@ class FileTile extends React.Component {
uri,
} = this.props
- this._isMounted = true;
-
- if (this.props.hideOnRemove) {
- this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
- }
-
if(!isResolvingUri && !claim && uri) {
resolveUri(uri)
}
}
- componentWillUnmount() {
- if (this._fileInfoSubscribeId) {
- lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId);
- }
- }
-
- onFileInfoUpdate(fileInfo) {
- if (!fileInfo && this._isMounted && this.props.hideOnRemove) {
- this.setState({
- isHidden: true
- });
- }
- }
-
handleMouseOver() {
if (this.props.obscureNsfw && this.props.metadata && this.props.metadata.nsfw) {
this.setState({
@@ -71,10 +48,6 @@ class FileTile extends React.Component {
}
render() {
- if (this.state.isHidden) {
- return null;
- }
-
const {
claim,
metadata,
@@ -86,18 +59,22 @@ class FileTile extends React.Component {
const uri = lbryuri.normalize(this.props.uri);
const isClaimed = !!claim;
+ const isClaimable = lbryuri.isClaimable(uri)
const title = isClaimed && metadata && metadata.title ? metadata.title : uri;
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
let onClick = () => navigate('/show', { uri })
let description = ""
if (isClaimed) {
- description = metadata.description
+ description = metadata && metadata.description
} else if (isResolvingUri) {
description = "Loading..."
} else if (showEmpty === FileTile.SHOW_EMPTY_PUBLISH) {
- onClick = () => navigate('/publish')
- description = This location is unclaimed - put something here!
+ onClick = () => navigate('/publish', { })
+ description =
+ This location is unused. { ' ' }
+ { isClaimable && Put something here! }
+
} else if (showEmpty === FileTile.SHOW_EMPTY_PENDING) {
description = This file is pending confirmation.
}
diff --git a/ui/js/component/router/index.jsx b/ui/js/component/router/index.jsx
index c75222949..48bf22ccc 100644
--- a/ui/js/component/router/index.jsx
+++ b/ui/js/component/router/index.jsx
@@ -2,14 +2,13 @@ import React from 'react';
import { connect } from 'react-redux';
import Router from './view.jsx';
import {
- selectCurrentPage
+ selectCurrentPage,
+ selectCurrentParams,
} from 'selectors/app.js';
const select = (state) => ({
+ params: selectCurrentParams(state),
currentPage: selectCurrentPage(state)
})
-const perform = {
-}
-
export default connect(select, null)(Router);
diff --git a/ui/js/component/router/view.jsx b/ui/js/component/router/view.jsx
index 8b3d66102..53dec32ef 100644
--- a/ui/js/component/router/view.jsx
+++ b/ui/js/component/router/view.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import SettingsPage from 'page/settings.js';
+import SettingsPage from 'page/settings';
import HelpPage from 'page/help';
import ReportPage from 'page/report.js';
import StartPage from 'page/start.js';
@@ -25,25 +25,26 @@ const route = (page, routesMap) => {
const Router = (props) => {
const {
currentPage,
+ params,
} = props;
return route(currentPage, {
- 'settings': ,
- 'help': ,
- 'report': ,
- 'downloaded': ,
- 'published': ,
- 'start': ,
- 'wallet': ,
- 'send': ,
- 'receive': ,
- 'show': ,
- 'channel': ,
- 'publish': ,
- 'developer': ,
- 'discover': ,
- 'rewards': ,
- 'search': ,
+ 'settings': ,
+ 'help': ,
+ 'report': ,
+ 'downloaded': ,
+ 'published': ,
+ 'start': ,
+ 'wallet': ,
+ 'send': ,
+ 'receive': ,
+ 'show': ,
+ 'channel': ,
+ 'publish': ,
+ 'developer': ,
+ 'discover': ,
+ 'rewards': ,
+ 'search': ,
})
}
diff --git a/ui/js/component/video/index.js b/ui/js/component/video/index.js
index 94978f702..5566da840 100644
--- a/ui/js/component/video/index.js
+++ b/ui/js/component/video/index.js
@@ -9,34 +9,46 @@ import {
selectCurrentModal,
} from 'selectors/app'
import {
- doWatchVideo,
+ doPurchaseUri,
doLoadVideo,
} from 'actions/content'
import {
- selectLoadingCurrentUri,
- selectCurrentUriFileReadyToPlay,
- selectCurrentUriIsPlaying,
- selectCurrentUriFileInfo,
- selectDownloadingCurrentUri,
+ makeSelectMetadataForUri
+} from 'selectors/claims'
+import {
+ makeSelectFileInfoForUri,
+ makeSelectLoadingForUri,
+ makeSelectDownloadingForUri,
} from 'selectors/file_info'
import {
- selectCurrentUriCostInfo,
+ makeSelectCostInfoForUri,
} from 'selectors/cost_info'
import Video from './view'
-const select = (state) => ({
- costInfo: selectCurrentUriCostInfo(state),
- fileInfo: selectCurrentUriFileInfo(state),
- modal: selectCurrentModal(state),
- isLoading: selectLoadingCurrentUri(state),
- readyToPlay: selectCurrentUriFileReadyToPlay(state),
- isDownloading: selectDownloadingCurrentUri(state),
-})
+
+const makeSelect = () => {
+ const selectCostInfo = makeSelectCostInfoForUri()
+ const selectFileInfo = makeSelectFileInfoForUri()
+ const selectIsLoading = makeSelectLoadingForUri()
+ const selectIsDownloading = makeSelectDownloadingForUri()
+ const selectMetadata = makeSelectMetadataForUri()
+
+ const select = (state, props) => ({
+ costInfo: selectCostInfo(state, props),
+ fileInfo: selectFileInfo(state, props),
+ metadata: selectMetadata(state, props),
+ modal: selectCurrentModal(state),
+ isLoading: selectIsLoading(state, props),
+ isDownloading: selectIsDownloading(state, props),
+ })
+
+ return select
+}
const perform = (dispatch) => ({
- loadVideo: () => dispatch(doLoadVideo()),
- watchVideo: (elem) => dispatch(doWatchVideo()),
+ loadVideo: (uri) => dispatch(doLoadVideo(uri)),
+ purchaseUri: (uri) => dispatch(doPurchaseUri(uri)),
closeModal: () => dispatch(doCloseModal()),
})
-export default connect(select, perform)(Video)
\ No newline at end of file
+export default connect(makeSelect, perform)(Video)
\ No newline at end of file
diff --git a/ui/js/component/video/view.jsx b/ui/js/component/video/view.jsx
index 5485f1a48..3d364bcc9 100644
--- a/ui/js/component/video/view.jsx
+++ b/ui/js/component/video/view.jsx
@@ -1,17 +1,21 @@
import React from 'react';
-import {
- Icon,
- Thumbnail,
-} from 'component/common';
import FilePrice from 'component/filePrice'
import Link from 'component/link';
import Modal from 'component/modal';
-class WatchLink extends React.Component {
- confirmPurchaseClick() {
+class VideoPlayButton extends React.Component {
+ onPurchaseConfirmed() {
this.props.closeModal()
this.props.startPlaying()
- this.props.loadVideo()
+ this.props.loadVideo(this.props.uri)
+ }
+
+ onWatchClick() {
+ this.props.purchaseUri(this.props.uri).then(() => {
+ if (!this.props.modal) {
+ this.props.startPlaying()
+ }
+ })
}
render() {
@@ -19,7 +23,6 @@ class WatchLink extends React.Component {
button,
label,
className,
- onWatchClick,
metadata,
metadata: {
title,
@@ -32,13 +35,21 @@ class WatchLink extends React.Component {
fileInfo,
} = this.props
+ /*
+ title={
+ isLoading ? "Video is Loading" :
+ !costInfo ? "Waiting on cost info..." :
+ fileInfo === undefined ? "Waiting on file info..." : ""
+ }
+ */
+
return (
+ onClick={this.onWatchClick.bind(this)} />
{modal}
You don't have enough LBRY credits to pay for this stream.
@@ -47,9 +58,9 @@ class WatchLink extends React.Component {
type="confirm"
isOpen={modal == 'affirmPurchase'}
contentLabel="Confirm Purchase"
- onConfirmed={this.confirmPurchaseClick.bind(this)}
+ onConfirmed={this.onPurchaseConfirmed.bind(this)}
onAborted={closeModal}>
- Are you sure you'd like to buy {this.props.metadata.title} for credits?
+ This will purchase {title} for credits.
@@ -67,16 +78,6 @@ class Video extends React.Component {
this.state = { isPlaying: false }
}
- onWatchClick() {
- this.props.watchVideo().then(() => {
- if (!this.props.modal) {
- this.setState({
- isPlaying: true
- })
- }
- })
- }
-
startPlaying() {
this.setState({
isPlaying: true
@@ -85,8 +86,6 @@ class Video extends React.Component {
render() {
const {
- readyToPlay = false,
- thumbnail,
metadata,
isLoading,
isDownloading,
@@ -96,6 +95,8 @@ class Video extends React.Component {
isPlaying = false,
} = this.state
+ const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0
+
let loadStatusMessage = ''
if (isLoading) {
@@ -105,39 +106,42 @@ class Video extends React.Component {
}
return (
- {
- isPlaying ?
- !readyToPlay ?
-
this is the world's worst loading screen and we shipped our software with it anyway...
{loadStatusMessage} :
-
:
+
{
+ isPlaying || isLoading ?
+ (!isReadyToPlay ?
+
this is the world's worst loading screen and we shipped our software with it anyway...
{loadStatusMessage} :
+
) :
-
+
}
);
}
}
-class VideoPlayer extends React.PureComponent {
+class VideoPlayer extends React.Component {
componentDidMount() {
const elem = this.refs.video
const {
+ autoplay,
downloadPath,
contentType,
} = this.props
const players = plyr.setup(elem)
- players[0].play()
+ if (autoplay) {
+ players[0].play()
+ }
}
render() {
const {
downloadPath,
contentType,
+ poster,
} = this.props
return (
-
diff --git a/ui/js/page/showPage/index.js b/ui/js/page/showPage/index.js
index bbaba13f6..563b9cff3 100644
--- a/ui/js/page/showPage/index.js
+++ b/ui/js/page/showPage/index.js
@@ -6,24 +6,27 @@ import {
doResolveUri,
} from 'actions/content'
import {
- selectCurrentUri,
-} from 'selectors/app'
-import {
- selectCurrentUriClaim,
+ makeSelectClaimForUri,
} from 'selectors/claims'
import {
- selectCurrentUriIsResolving,
+ makeSelectIsResolvingForUri,
} from 'selectors/content'
import ShowPage from './view'
-const select = (state, props) => ({
- claim: selectCurrentUriClaim(state),
- uri: selectCurrentUri(state),
- isResolvingUri: selectCurrentUriIsResolving(state)
-})
+const makeSelect = () => {
+ const selectClaim = makeSelectClaimForUri(),
+ selectIsResolving = makeSelectIsResolvingForUri();
+
+ const select = (state, props) => ({
+ claim: selectClaim(state, props),
+ isResolvingUri: selectIsResolving(state, props)
+ })
+
+ return select
+}
const perform = (dispatch) => ({
resolveUri: (uri) => dispatch(doResolveUri(uri))
})
-export default connect(select, perform)(ShowPage)
+export default connect(makeSelect, perform)(ShowPage)
diff --git a/ui/js/page/showPage/view.jsx b/ui/js/page/showPage/view.jsx
index 7e84d3d30..96983139c 100644
--- a/ui/js/page/showPage/view.jsx
+++ b/ui/js/page/showPage/view.jsx
@@ -1,7 +1,9 @@
import React from 'react';
+import lbryuri from 'lbryuri'
import {
BusyMessage,
} from 'component/common';
+import ChannelPage from 'page/channel'
import FilePage from 'page/filePage'
class ShowPage extends React.Component{
@@ -21,7 +23,7 @@ class ShowPage extends React.Component{
uri,
} = props
- if(!isResolvingUri && !claim && uri) {
+ if(!isResolvingUri && claim === undefined && uri) {
resolveUri(uri)
}
}
@@ -35,22 +37,22 @@ class ShowPage extends React.Component{
let innerContent = "";
- if (isResolvingUri) {
+ if (isResolvingUri || !claim) {
innerContent =
- :
+ { isResolvingUri && }
+ { claim === null && There's nothing at this location. }
- ;
+
}
- else if (claim && claim.whatever) {
- innerContent = "channel"
- // innerContent =
+ else if (claim.name.length && claim.name[0] === '@') {
+ innerContent =
}
else if (claim) {
- innerContent =
+ innerContent =
}
return (
@@ -59,4 +61,4 @@ class ShowPage extends React.Component{
}
}
-export default ShowPage
+export default ShowPage
\ No newline at end of file
diff --git a/ui/js/reducers/app.js b/ui/js/reducers/app.js
index 883ff068f..e3070ed40 100644
--- a/ui/js/reducers/app.js
+++ b/ui/js/reducers/app.js
@@ -9,7 +9,6 @@ const defaultState = {
upgradeSkipped: sessionStorage.getItem('upgradeSkipped'),
daemonReady: false,
obscureNsfw: !lbry.getClientSetting('showNsfw'),
- hidePrice: false,
hasSignature: false,
}
diff --git a/ui/js/reducers/availability.js b/ui/js/reducers/availability.js
index 6c0e28a8b..6fe6e4ddf 100644
--- a/ui/js/reducers/availability.js
+++ b/ui/js/reducers/availability.js
@@ -9,10 +9,8 @@ reducers[types.FETCH_AVAILABILITY_STARTED] = function(state, action) {
uri,
} = action.data
const newFetching = Object.assign({}, state.fetching)
- const newByUri = Object.assign({}, newFetching.byUri)
- newByUri[uri] = true
- newFetching.byUri = newByUri
+ newFetching[uri] = true
return Object.assign({}, state, {
fetching: newFetching,
@@ -24,12 +22,11 @@ reducers[types.FETCH_AVAILABILITY_COMPLETED] = function(state, action) {
uri,
availability,
} = action.data
+
const newFetching = Object.assign({}, state.fetching)
- const newFetchingByUri = Object.assign({}, newFetching.byUri)
const newAvailabilityByUri = Object.assign({}, state.byUri)
- delete newFetchingByUri[uri]
- newFetching.byUri = newFetchingByUri
+ delete newFetching[uri]
newAvailabilityByUri[uri] = availability
return Object.assign({}, state, {
diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js
index c758052bc..6acf0f111 100644
--- a/ui/js/reducers/claims.js
+++ b/ui/js/reducers/claims.js
@@ -8,30 +8,69 @@ const defaultState = {
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
const {
uri,
+ certificate,
claim,
} = action.data
- const newByUri = Object.assign({}, state.byUri)
- newByUri[uri] = claim
+ const newClaims = Object.assign({}, state.claimsByUri)
+
+ newClaims[uri] = claim
+
+ //This needs a sanity boost...
+ if (certificate !== undefined && claim === undefined) {
+ const uriParts = lbryuri.parse(uri);
+ // newChannelClaims[uri] = certificate
+ if (claim === undefined) {
+ newClaims[uri] = certificate
+ }
+ }
+
return Object.assign({}, state, {
- byUri: newByUri,
+ claimsByUri: newClaims
})
}
-reducers[types.FETCH_MY_CLAIMS_COMPLETED] = function(state, action) {
- const {
- claims,
- } = action.data
- const newMine = Object.assign({}, state.mine)
- const newById = Object.assign({}, newMine.byId)
-
- claims.forEach(claim => {
- newById[claim.claim_id] = claim
+reducers[types.RESOLVE_URI_CANCELED] = function(state, action) {
+ const uri = action.data.uri
+ const newClaims = Object.assign({}, state.claimsByUri)
+ delete newClaims[uri]
+ return Object.assign({}, state, {
+ claimsByUri: newClaims
})
- newMine.byId = newById
+}
+
+
+reducers[types.CLAIM_LIST_MINE_STARTED] = function(state, action) {
+ return Object.assign({}, state, {
+ isClaimListMinePending: true
+ })
+}
+
+reducers[types.CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
+ const myClaims = Object.assign({}, state.myClaims)
+ action.data.claims.forEach((claim) => {
+ myClaims[claim.claim_id] = claim
+ })
+ return Object.assign({}, state, {
+ isClaimListMinePending: false,
+ myClaims: myClaims
+ })
+}
+
+reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
+ const {
+ uri,
+ claims
+ } = action.data
+
+ const newClaims = Object.assign({}, state.claimsByChannel)
+
+ if (claims !== undefined) {
+ newClaims[uri] = claims
+ }
return Object.assign({}, state, {
- mine: newMine,
+ claimsByChannel: newClaims
})
}
diff --git a/ui/js/reducers/content.js b/ui/js/reducers/content.js
index 8fee4c234..117b6c830 100644
--- a/ui/js/reducers/content.js
+++ b/ui/js/reducers/content.js
@@ -30,7 +30,7 @@ reducers[types.RESOLVE_URI_STARTED] = function(state, action) {
const oldResolving = state.resolvingUris || []
const newResolving = Object.assign([], oldResolving)
- if (newResolving.indexOf(uri) == -1) newResolving.push(uri)
+ if (newResolving.indexOf(uri) === -1) newResolving.push(uri)
return Object.assign({}, state, {
resolvingUris: newResolving
@@ -48,51 +48,14 @@ reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
...resolvingUris.slice(index + 1)
]
- const newState = Object.assign({}, state, {
+ return Object.assign({}, state, {
resolvingUris: newResolvingUris,
})
-
- return Object.assign({}, state, newState)
}
-reducers[types.FETCH_DOWNLOADED_CONTENT_STARTED] = function(state, action) {
- return Object.assign({}, state, {
- fetchingDownloadedContent: true,
- })
-}
-reducers[types.FETCH_DOWNLOADED_CONTENT_COMPLETED] = function(state, action) {
- const {
- fileInfos
- } = action.data
- const newDownloadedContent = Object.assign({}, state.downloadedContent, {
- fileInfos
- })
-
- return Object.assign({}, state, {
- downloadedContent: newDownloadedContent,
- fetchingDownloadedContent: false,
- })
-}
-
-reducers[types.FETCH_PUBLISHED_CONTENT_STARTED] = function(state, action) {
- return Object.assign({}, state, {
- fetchingPublishedContent: true,
- })
-}
-
-reducers[types.FETCH_PUBLISHED_CONTENT_COMPLETED] = function(state, action) {
- const {
- fileInfos
- } = action.data
- const newPublishedContent = Object.assign({}, state.publishedContent, {
- fileInfos
- })
-
- return Object.assign({}, state, {
- publishedContent: newPublishedContent,
- fetchingPublishedContent: false,
- })
+reducers[types.RESOLVE_URI_CANCELED] = function(state, action) {
+ return reducers[types.RESOLVE_URI_COMPLETED](state, action)
}
export default function reducer(state = defaultState, action) {
diff --git a/ui/js/reducers/file_info.js b/ui/js/reducers/file_info.js
index 292508a2b..811c829ef 100644
--- a/ui/js/reducers/file_info.js
+++ b/ui/js/reducers/file_info.js
@@ -5,13 +5,35 @@ const reducers = {}
const defaultState = {
}
+reducers[types.FILE_LIST_STARTED] = function(state, action) {
+ return Object.assign({}, state, {
+ isFileListPending: true,
+ })
+}
+
+reducers[types.FILE_LIST_COMPLETED] = function(state, action) {
+ const {
+ fileInfos,
+ } = action.data
+
+ const newFileInfos = Object.assign({}, state.fileInfos)
+ fileInfos.forEach((fileInfo) => {
+ newFileInfos[fileInfo.outpoint] = fileInfo
+ })
+
+ return Object.assign({}, state, {
+ isFileListPending: false,
+ fileInfos: newFileInfos
+ })
+}
+
reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) {
const {
- uri,
+ outpoint
} = action.data
const newFetching = Object.assign({}, state.fetching)
- newFetching[uri] = true
+ newFetching[outpoint] = true
return Object.assign({}, state, {
fetching: newFetching,
@@ -20,17 +42,18 @@ reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) {
reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
const {
- uri,
fileInfo,
+ outpoint,
} = action.data
- const newByUri = Object.assign({}, state.byUri)
+
+ const newFileInfos = Object.assign({}, state.fileInfos)
const newFetching = Object.assign({}, state.fetching)
- newByUri[uri] = fileInfo || {}
- delete newFetching[uri]
+ newFileInfos[outpoint] = fileInfo
+ delete newFetching[outpoint]
return Object.assign({}, state, {
- byUri: newByUri,
+ fileInfos: newFileInfos,
fetching: newFetching,
})
}
@@ -38,93 +61,74 @@ reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
reducers[types.DOWNLOADING_STARTED] = function(state, action) {
const {
uri,
+ outpoint,
fileInfo,
} = action.data
- const newByUri = Object.assign({}, state.byUri)
- const newDownloading = Object.assign({}, state.downloading)
- const newDownloadingByUri = Object.assign({}, newDownloading.byUri)
- const newLoading = Object.assign({}, state.loading)
- const newLoadingByUri = Object.assign({}, newLoading)
- newDownloadingByUri[uri] = true
- newDownloading.byUri = newDownloadingByUri
- newByUri[uri] = fileInfo
- delete newLoadingByUri[uri]
- newLoading.byUri = newLoadingByUri
+ const newFileInfos = Object.assign({}, state.fileInfos)
+ const newDownloading = Object.assign({}, state.urisDownloading)
+ const newLoading = Object.assign({}, state.urisLoading)
+
+ newDownloading[uri] = true
+ newFileInfos[outpoint] = fileInfo
+ delete newLoading[uri]
return Object.assign({}, state, {
- downloading: newDownloading,
- byUri: newByUri,
- loading: newLoading,
+ urisDownloading: newDownloading,
+ urisLoading: newLoading,
+ fileInfos: newFileInfos,
})
}
reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) {
const {
uri,
+ outpoint,
fileInfo,
} = action.data
- const newByUri = Object.assign({}, state.byUri)
- const newDownloading = Object.assign({}, state.downloading)
- newByUri[uri] = fileInfo
+ const newFileInfos = Object.assign({}, state.fileInfos)
+ const newDownloading = Object.assign({}, state.urisDownloading)
+
+ newFileInfos[outpoint] = fileInfo
newDownloading[uri] = true
return Object.assign({}, state, {
- byUri: newByUri,
- downloading: newDownloading
+ fileInfos: newFileInfos,
+ urisDownloading: newDownloading
})
}
reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
const {
uri,
+ outpoint,
fileInfo,
} = action.data
- const newByUri = Object.assign({}, state.byUri)
- const newDownloading = Object.assign({}, state.downloading)
- const newDownloadingByUri = Object.assign({}, newDownloading.byUri)
- newByUri[uri] = fileInfo
- delete newDownloadingByUri[uri]
- newDownloading.byUri = newDownloadingByUri
+ const newFileInfos = Object.assign({}, state.fileInfos)
+ const newDownloading = Object.assign({}, state.urisDownloading)
+
+ newFileInfos[outpoint] = fileInfo
+ delete newDownloading[uri]
return Object.assign({}, state, {
- byUri: newByUri,
- downloading: newDownloading,
+ fileInfos: newFileInfos,
+ urisDownloading: newDownloading,
})
}
-reducers[types.DELETE_FILE_STARTED] = function(state, action) {
+reducers[types.FILE_DELETE] = function(state, action) {
const {
- uri,
+ outpoint,
} = action.data
- const newDeleting = Object.assign({}, state.deleting)
- const newByUri = Object.assign({}, newDeleting.byUri)
- newByUri[uri] = true
- newDeleting.byUri = newByUri
+ const newFileInfos = Object.assign({}, state.fileInfos)
+
+ delete newFileInfos[outpoint]
return Object.assign({}, state, {
- deleting: newDeleting,
- })
-}
-
-reducers[types.DELETE_FILE_COMPLETED] = function(state, action) {
- const {
- uri,
- } = action.data
- const newDeleting = Object.assign({}, state.deleting)
- const newDeletingByUri = Object.assign({}, newDeleting.byUri)
- const newByUri = Object.assign({}, state.byUri)
-
- delete newDeletingByUri[uri]
- newDeleting.byUri = newDeletingByUri
- delete newByUri[uri]
-
- return Object.assign({}, state, {
- deleting: newDeleting,
- byUri: newByUri,
+ fileInfos: newFileInfos,
})
}
@@ -132,14 +136,13 @@ reducers[types.LOADING_VIDEO_STARTED] = function(state, action) {
const {
uri,
} = action.data
- const newLoading = Object.assign({}, state.loading)
- const newByUri = Object.assign({}, newLoading.byUri)
- newByUri[uri] = true
- newLoading.byUri = newByUri
+ const newLoading = Object.assign({}, state.urisLoading)
+
+ newLoading[uri] = true
return Object.assign({}, state, {
- loading: newLoading,
+ urisLoading: newLoading,
})
}
@@ -147,54 +150,13 @@ reducers[types.LOADING_VIDEO_FAILED] = function(state, action) {
const {
uri,
} = action.data
- const newLoading = Object.assign({}, state.loading)
- const newByUri = Object.assign({}, newLoading.byUri)
- delete newByUri[uri]
- newLoading.byUri = newByUri
+ const newLoading = Object.assign({}, state.urisLoading)
+
+ delete newLoading[uri]
return Object.assign({}, state, {
- loading: newLoading,
- })
-}
-
-reducers[types.FETCH_DOWNLOADED_CONTENT_COMPLETED] = function(state, action) {
- const {
- fileInfos,
- } = action.data
- const newByUri = Object.assign({}, state.byUri)
-
- fileInfos.forEach(fileInfo => {
- const uri = lbryuri.build({
- channelName: fileInfo.channel_name,
- contentName: fileInfo.name,
- })
-
- newByUri[uri] = fileInfo
- })
-
- return Object.assign({}, state, {
- byUri: newByUri
- })
-}
-
-reducers[types.FETCH_PUBLISHED_CONTENT_COMPLETED] = function(state, action) {
- const {
- fileInfos
- } = action.data
- const newByUri = Object.assign({}, state.byUri)
-
- fileInfos.forEach(fileInfo => {
- const uri = lbryuri.build({
- channelName: fileInfo.channel_name,
- contentName: fileInfo.name,
- })
-
- newByUri[uri] = fileInfo
- })
-
- return Object.assign({}, state, {
- byUri: newByUri
+ urisLoading: newLoading,
})
}
diff --git a/ui/js/reducers/certificates.js b/ui/js/reducers/settings.js
similarity index 50%
rename from ui/js/reducers/certificates.js
rename to ui/js/reducers/settings.js
index ebeaed986..68c684f7e 100644
--- a/ui/js/reducers/certificates.js
+++ b/ui/js/reducers/settings.js
@@ -1,25 +1,14 @@
import * as types from 'constants/action_types'
const reducers = {}
-const defaultState = {
-}
+const defaultState = {}
-reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
- const {
- uri,
- certificate,
- } = action.data
- if (!certificate) return state
-
- const newByUri = Object.assign({}, state.byUri)
-
- newByUri[uri] = certificate
+reducers[types.DAEMON_SETTINGS_RECEIVED] = function(state, action) {
return Object.assign({}, state, {
- byUri: newByUri,
+ daemonSettings: action.data.settings
})
}
-
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
diff --git a/ui/js/selectors/app.js b/ui/js/selectors/app.js
index 7692adae7..f0af3b5a1 100644
--- a/ui/js/selectors/app.js
+++ b/ui/js/selectors/app.js
@@ -31,27 +31,10 @@ export const selectCurrentParams = createSelector(
}
)
-export const selectCurrentUri = createSelector(
- selectCurrentPath,
- (path) => {
- if (path.match(/=/)) {
- return path.split('=')[1]
- }
- else {
- return undefined
- }
- }
-)
-
-export const selectCurrentUriTitle = createSelector(
- _selectState,
- (state) => "fix me"
-)
-
export const selectPageTitle = createSelector(
selectCurrentPage,
- selectCurrentUri,
- (page, uri) => {
+ selectCurrentParams,
+ (page, params) => {
switch (page) {
case 'search':
return 'Search'
@@ -67,7 +50,7 @@ export const selectPageTitle = createSelector(
case 'rewards':
return page.charAt(0).toUpperCase() + page.slice(1)
case 'show':
- return lbryuri.normalize(uri)
+ return lbryuri.normalize(params.uri)
case 'downloaded':
return 'Downloads & Purchases'
case 'published':
@@ -195,11 +178,6 @@ export const selectUpgradeDownloadItem = createSelector(
(state) => state.downloadItem
)
-export const selectSearchTerm = createSelector(
- _selectState,
- (state) => state.searchTerm
-)
-
export const selectError = createSelector(
_selectState,
(state) => state.error
@@ -213,14 +191,4 @@ export const selectDaemonReady = createSelector(
export const selectObscureNsfw = createSelector(
_selectState,
(state) => !!state.obscureNsfw
-)
-
-export const selectHidePrice = createSelector(
- _selectState,
- (state) => !!state.hidePrice
-)
-
-export const selectHasSignature = createSelector(
- _selectState,
- (state) => !!state.hasSignature
-)
+)
\ No newline at end of file
diff --git a/ui/js/selectors/availability.js b/ui/js/selectors/availability.js
index 0eaebc318..8b65c5ec6 100644
--- a/ui/js/selectors/availability.js
+++ b/ui/js/selectors/availability.js
@@ -4,7 +4,6 @@ import {
import {
selectDaemonReady,
selectCurrentPage,
- selectCurrentUri,
} from 'selectors/app'
const _selectState = state => state.availability
@@ -14,29 +13,24 @@ export const selectAvailabilityByUri = createSelector(
(state) => state.byUri || {}
)
+const selectAvailabilityForUri = (state, props) => {
+ return selectAvailabilityByUri(state)[props.uri]
+}
+
+export const makeSelectIsAvailableForUri = () => {
+ return createSelector(
+ selectAvailabilityForUri,
+ (availability) => availability === undefined ? undefined : availability > 0
+ )
+}
+
export const selectFetchingAvailability = createSelector(
_selectState,
(state) => state.fetching || {}
)
-export const selectFetchingAvailabilityByUri = createSelector(
- selectFetchingAvailability,
- (fetching) => fetching.byUri || {}
-)
-
-const selectAvailabilityForUri = (state, props) => {
- return selectAvailabilityByUri(state)[props.uri]
-}
-
-export const makeSelectAvailabilityForUri = () => {
- return createSelector(
- selectAvailabilityForUri,
- (availability) => availability
- )
-}
-
const selectFetchingAvailabilityForUri = (state, props) => {
- return selectFetchingAvailabilityByUri(state)[props.uri]
+ return selectFetchingAvailability(state)[props.uri]
}
export const makeSelectFetchingAvailabilityForUri = () => {
@@ -44,16 +38,4 @@ export const makeSelectFetchingAvailabilityForUri = () => {
selectFetchingAvailabilityForUri,
(fetching) => fetching
)
-}
-
-export const selectFetchingAvailabilityForCurrentUri = createSelector(
- selectCurrentUri,
- selectFetchingAvailabilityByUri,
- (uri, byUri) => byUri[uri]
-)
-
-export const selectAvailabilityForCurrentUri = createSelector(
- selectCurrentUri,
- selectAvailabilityByUri,
- (uri, byUri) => byUri[uri]
-)
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/ui/js/selectors/claims.js b/ui/js/selectors/claims.js
index a755eea18..9a76b872e 100644
--- a/ui/js/selectors/claims.js
+++ b/ui/js/selectors/claims.js
@@ -2,28 +2,17 @@ import {
createSelector,
} from 'reselect'
import lbryuri from 'lbryuri'
-import {
- selectCurrentUri,
-} from 'selectors/app'
export const _selectState = state => state.claims || {}
export const selectClaimsByUri = createSelector(
_selectState,
- (state) => state.byUri || {}
+ (state) => state.claimsByUri || {}
)
-export const selectCurrentUriClaim = createSelector(
- selectCurrentUri,
- selectClaimsByUri,
- (uri, byUri) => byUri[uri]
-)
-
-export const selectCurrentUriClaimOutpoint = createSelector(
- selectCurrentUriClaim,
- (claim) => {
- return claim ? `${claim.txid}:${claim.nout}` : null
- }
+export const selectAllClaimsByChannel = createSelector(
+ _selectState,
+ (state) => state.claimsByChannel || {}
)
const selectClaimForUri = (state, props) => {
@@ -38,11 +27,22 @@ export const makeSelectClaimForUri = () => {
)
}
+export const selectClaimsInChannelForUri = (state, props) => {
+ return selectAllClaimsByChannel(state)[props.uri]
+}
+
+export const makeSelectClaimsInChannelForUri = () => {
+ return createSelector(
+ selectClaimsInChannelForUri,
+ (claims) => claims
+ )
+}
+
const selectMetadataForUri = (state, props) => {
const claim = selectClaimForUri(state, props)
const metadata = claim && claim.value && claim.value.stream && claim.value.stream.metadata
- return metadata ? metadata : undefined
+ return metadata ? metadata : (claim === undefined ? undefined : null)
}
export const makeSelectMetadataForUri = () => {
@@ -56,7 +56,7 @@ const selectSourceForUri = (state, props) => {
const claim = selectClaimForUri(state, props)
const source = claim && claim.value && claim.value.stream && claim.value.stream.source
- return source ? source : undefined
+ return source ? source : (claim === undefined ? undefined : null)
}
export const makeSelectSourceForUri = () => {
@@ -66,26 +66,32 @@ export const makeSelectSourceForUri = () => {
)
}
-export const selectMyClaims = createSelector(
+export const makeSelectContentTypeForUri = () => {
+ return createSelector(
+ selectSourceForUri,
+ (source) => source ? source.contentType : source
+ )
+}
+
+export const selectClaimListMineIsPending = createSelector(
_selectState,
- (state) => state.mine || {}
+ (state) => state.isClaimListMinePending
)
-export const selectMyClaimsById = createSelector(
- selectMyClaims,
- (mine) => mine.byId || {}
+export const selectMyClaims = createSelector(
+ _selectState,
+ (state) => state.myClaims || {}
)
export const selectMyClaimsOutpoints = createSelector(
- selectMyClaimsById,
- (byId) => {
- const outpoints = []
- Object.keys(byId).forEach(key => {
- const claim = byId[key]
- const outpoint = `${claim.txid}:${claim.nout}`
- outpoints.push(outpoint)
- })
+ selectMyClaims,
+ (claims) => {
+ if (!claims) {
+ return []
+ }
- return outpoints
+ return Object.values(claims).map((claim) => {
+ return `${claim.txid}:${claim.nout}`
+ })
}
)
diff --git a/ui/js/selectors/content.js b/ui/js/selectors/content.js
index 6ee54bf94..61efa2e1f 100644
--- a/ui/js/selectors/content.js
+++ b/ui/js/selectors/content.js
@@ -2,7 +2,6 @@ import { createSelector } from 'reselect'
import {
selectDaemonReady,
selectCurrentPage,
- selectCurrentUri,
} from 'selectors/app'
export const _selectState = state => state.content || {}
@@ -17,56 +16,18 @@ export const selectFetchingFeaturedUris = createSelector(
(state) => !!state.fetchingFeaturedContent
)
-export const selectFetchingFileInfos = createSelector(
- _selectState,
- (state) => state.fetchingFileInfos || {}
-)
-
-export const selectFetchingDownloadedContent = createSelector(
- _selectState,
- (state) => !!state.fetchingDownloadedContent
-)
-
-export const selectDownloadedContent = createSelector(
- _selectState,
- (state) => state.downloadedContent || {}
-)
-
-export const selectDownloadedContentFileInfos = createSelector(
- selectDownloadedContent,
- (downloadedContent) => downloadedContent.fileInfos || []
-)
-
-export const selectFetchingPublishedContent = createSelector(
- _selectState,
- (state) => !!state.fetchingPublishedContent
-)
-
-export const selectPublishedContent = createSelector(
- _selectState,
- (state) => state.publishedContent || {}
-)
-
-
export const selectResolvingUris = createSelector(
_selectState,
(state) => state.resolvingUris || []
)
-
-export const selectCurrentUriIsResolving = createSelector(
- selectCurrentUri,
- selectResolvingUris,
- (uri, resolvingUris) => resolvingUris.indexOf(uri) != -1
-)
-
const selectResolvingUri = (state, props) => {
return selectResolvingUris(state).indexOf(props.uri) != -1
}
-export const makeSelectResolvingUri = () => {
+export const makeSelectIsResolvingForUri = () => {
return createSelector(
selectResolvingUri,
(resolving) => resolving
)
-}
+}
\ No newline at end of file
diff --git a/ui/js/selectors/cost_info.js b/ui/js/selectors/cost_info.js
index f48fa3438..242ec6b0d 100644
--- a/ui/js/selectors/cost_info.js
+++ b/ui/js/selectors/cost_info.js
@@ -1,8 +1,4 @@
import { createSelector } from 'reselect'
-import {
- selectCurrentUri,
- selectCurrentPage,
-} from 'selectors/app'
export const _selectState = state => state.costInfo || {}
@@ -11,24 +7,7 @@ export const selectAllCostInfoByUri = createSelector(
(state) => state.byUri || {}
)
-export const selectCurrentUriCostInfo = createSelector(
- selectCurrentUri,
- selectAllCostInfoByUri,
- (uri, byUri) => byUri[uri] || {}
-)
-
-export const selectFetchingCostInfo = createSelector(
- _selectState,
- (state) => state.fetching || {}
-)
-
-export const selectFetchingCurrentUriCostInfo = createSelector(
- selectCurrentUri,
- selectFetchingCostInfo,
- (uri, byUri) => !!byUri[uri]
-)
-
-const selectCostInfoForUri = (state, props) => {
+export const selectCostInfoForUri = (state, props) => {
return selectAllCostInfoByUri(state)[props.uri]
}
diff --git a/ui/js/selectors/file_info.js b/ui/js/selectors/file_info.js
index b41d5ae49..cf7895d56 100644
--- a/ui/js/selectors/file_info.js
+++ b/ui/js/selectors/file_info.js
@@ -1,72 +1,38 @@
+import lbry from 'lbry'
import {
createSelector,
} from 'reselect'
import {
- selectCurrentUri,
- selectCurrentPage,
-} from 'selectors/app'
-import {
+ selectClaimsByUri,
+ selectClaimListMineIsPending,
selectMyClaimsOutpoints,
} from 'selectors/claims'
export const _selectState = state => state.fileInfo || {}
-export const selectAllFileInfoByUri = createSelector(
+export const selectAllFileInfos = createSelector(
_selectState,
- (state) => state.byUri || {}
+ (state) => state.fileInfos || {}
)
-export const selectCurrentUriRawFileInfo = createSelector(
- selectCurrentUri,
- selectAllFileInfoByUri,
- (uri, byUri) => byUri[uri]
-)
-
-export const selectCurrentUriFileInfo = createSelector(
- selectCurrentUriRawFileInfo,
- (fileInfo) => fileInfo
-)
-
-export const selectFetchingFileInfo = createSelector(
+export const selectFileListIsPending = createSelector(
_selectState,
- (state) => state.fetching || {}
+ (state) => state.isFileListPending
)
-export const selectFetchingCurrentUriFileInfo = createSelector(
- selectCurrentUri,
- selectFetchingFileInfo,
- (uri, byUri) => !!byUri[uri]
+export const selectFileListDownloadedOrPublishedIsPending = createSelector(
+ selectFileListIsPending,
+ selectClaimListMineIsPending,
+ (isFileListPending, isClaimListMinePending) => isFileListPending || isClaimListMinePending
)
-export const selectDownloading = createSelector(
- _selectState,
- (state) => state.downloading || {}
-)
+export const selectFileInfoForUri = (state, props) => {
+ const claims = selectClaimsByUri(state),
+ claim = claims[props.uri],
+ fileInfos = selectAllFileInfos(state),
+ outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined
-export const selectDownloadingByUri = createSelector(
- selectDownloading,
- (downloading) => downloading.byUri || {}
-)
-
-export const selectDownloadingCurrentUri = createSelector(
- selectCurrentUri,
- selectDownloadingByUri,
- (uri, byUri) => !!byUri[uri]
-)
-
-export const selectCurrentUriIsDownloaded = createSelector(
- selectCurrentUriFileInfo,
- (fileInfo) => {
- if (!fileInfo) return false
- if (!fileInfo.completed) return false
- if (!fileInfo.written_bytes > 0) return false
-
- return true
- }
-)
-
-const selectFileInfoForUri = (state, props) => {
- return selectAllFileInfoByUri(state)[props.uri]
+ return outpoint && fileInfos ? fileInfos[outpoint] : undefined
}
export const makeSelectFileInfoForUri = () => {
@@ -76,8 +42,13 @@ export const makeSelectFileInfoForUri = () => {
)
}
+export const selectUrisDownloading = createSelector(
+ _selectState,
+ (state) => state.urisDownloading || {}
+)
+
const selectDownloadingForUri = (state, props) => {
- const byUri = selectDownloadingByUri(state)
+ const byUri = selectUrisDownloading(state)
return byUri[props.uri]
}
@@ -88,31 +59,13 @@ export const makeSelectDownloadingForUri = () => {
)
}
-export const selectLoading = createSelector(
+export const selectUrisLoading = createSelector(
_selectState,
- (state) => state.loading || {}
-)
-
-export const selectLoadingByUri = createSelector(
- selectLoading,
- (loading) => loading.byUri || {}
-)
-
-export const selectLoadingCurrentUri = createSelector(
- selectLoadingByUri,
- selectCurrentUri,
- (byUri, uri) => !!byUri[uri]
-)
-
-// TODO make this smarter so it doesn't start playing and immediately freeze
-// while downloading more.
-export const selectCurrentUriFileReadyToPlay = createSelector(
- selectCurrentUriFileInfo,
- (fileInfo) => (fileInfo || {}).written_bytes > 0
+ (state) => state.urisLoading || {}
)
const selectLoadingForUri = (state, props) => {
- const byUri = selectLoadingByUri(state)
+ const byUri = selectUrisLoading(state)
return byUri[props.uri]
}
@@ -123,36 +76,38 @@ export const makeSelectLoadingForUri = () => {
)
}
-export const selectDownloadedFileInfo = createSelector(
- selectAllFileInfoByUri,
- (byUri) => {
+export const selectFileInfosDownloaded = createSelector(
+ selectAllFileInfos,
+ selectMyClaimsOutpoints,
+ (fileInfos, myClaimOutpoints) => {
const fileInfoList = []
- Object.keys(byUri).forEach(key => {
- const fileInfo = byUri[key]
-
- if (fileInfo.completed || fileInfo.written_bytes) {
+ Object.values(fileInfos).forEach(fileInfo => {
+ if (fileInfo && myClaimOutpoints.indexOf(fileInfo.outpoint) === -1 && (fileInfo.completed || fileInfo.written_bytes)) {
fileInfoList.push(fileInfo)
}
})
-
return fileInfoList
}
)
-export const selectPublishedFileInfo = createSelector(
- selectAllFileInfoByUri,
- selectMyClaimsOutpoints,
- (byUri, outpoints) => {
- const fileInfos = []
- outpoints.forEach(outpoint => {
- Object.keys(byUri).forEach(key => {
- const fileInfo = byUri[key]
- if (fileInfo.outpoint == outpoint) {
- fileInfos.push(fileInfo)
- }
- })
- })
-
- return fileInfos
+export const selectFileInfosPendingPublish = createSelector(
+ _selectState,
+ (state) => {
+ return lbry.getPendingPublishes()
+ }
+)
+
+export const selectFileInfosPublished = createSelector(
+ selectAllFileInfos,
+ selectFileInfosPendingPublish,
+ selectMyClaimsOutpoints,
+ (allFileInfos, pendingFileInfos, outpoints) => {
+ const fileInfos = []
+ outpoints.forEach(outpoint => {
+ if (allFileInfos[outpoint]) {
+ fileInfos.push(allFileInfos[outpoint])
+ }
+ })
+ return [...fileInfos, ...pendingFileInfos]
}
)
diff --git a/ui/js/selectors/search.js b/ui/js/selectors/search.js
index 12dec7658..6bfa70b7d 100644
--- a/ui/js/selectors/search.js
+++ b/ui/js/selectors/search.js
@@ -2,7 +2,6 @@ import { createSelector } from 'reselect'
import {
selectPageTitle,
selectCurrentPage,
- selectCurrentUri
} from 'selectors/app'
export const _selectState = state => state.search || {}
@@ -43,8 +42,7 @@ export const selectWunderBarAddress = createSelector(
export const selectWunderBarIcon = createSelector(
selectCurrentPage,
- selectCurrentUri,
- (page, uri) => {
+ (page) => {
switch (page) {
case 'search':
return 'icon-search'
diff --git a/ui/js/selectors/settings.js b/ui/js/selectors/settings.js
new file mode 100644
index 000000000..850259043
--- /dev/null
+++ b/ui/js/selectors/settings.js
@@ -0,0 +1,20 @@
+import {
+ createSelector,
+} from 'reselect'
+
+const _selectState = state => state.settings || {}
+
+export const selectDaemonSettings = createSelector(
+ _selectState,
+ (state) => state.daemonSettings || {}
+)
+
+export const selectClientSettings = createSelector(
+ _selectState,
+ (state) => state.clientSettings
+)
+
+export const selectSettingsIsGenerous = createSelector(
+ selectDaemonSettings,
+ (settings) => settings && settings.is_generous_host
+)
\ No newline at end of file
diff --git a/ui/js/store.js b/ui/js/store.js
index 462c6a5d0..479fdb49d 100644
--- a/ui/js/store.js
+++ b/ui/js/store.js
@@ -7,13 +7,13 @@ import {
} from 'redux-logger'
import appReducer from 'reducers/app';
import availabilityReducer from 'reducers/availability'
-import certificatesReducer from 'reducers/certificates'
import claimsReducer from 'reducers/claims'
import contentReducer from 'reducers/content';
import costInfoReducer from 'reducers/cost_info'
import fileInfoReducer from 'reducers/file_info'
import rewardsReducer from 'reducers/rewards'
import searchReducer from 'reducers/search'
+import settingsReducer from 'reducers/settings'
import walletReducer from 'reducers/wallet'
function isFunction(object) {
@@ -49,13 +49,13 @@ function enableBatching(reducer) {
const reducers = redux.combineReducers({
app: appReducer,
availability: availabilityReducer,
- certificates: certificatesReducer,
claims: claimsReducer,
fileInfo: fileInfoReducer,
content: contentReducer,
costInfo: costInfoReducer,
rewards: rewardsReducer,
search: searchReducer,
+ settings: settingsReducer,
wallet: walletReducer,
});
diff --git a/ui/package.json b/ui/package.json
index e73d866f6..72d7bdde1 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -22,7 +22,6 @@
"babel-cli": "^6.11.4",
"babel-preset-es2015": "^6.13.2",
"babel-preset-react": "^6.11.1",
- "mediaelement": "^2.23.4",
"node-sass": "^3.8.0",
"plyr": "^2.0.12",
"rc-progress": "^2.0.6",
diff --git a/ui/scss/_gui.scss b/ui/scss/_gui.scss
index 3d85f4b22..f4ededba0 100644
--- a/ui/scss/_gui.scss
+++ b/ui/scss/_gui.scss
@@ -143,6 +143,14 @@ p
font-style: italic;
}
+/*should be redone/moved*/
+.file-list__header {
+ .busy-indicator {
+ float: left;
+ margin-top: 12px;
+ }
+}
+
.sort-section {
display: block;
margin-bottom: $spacing-vertical * 2/3;
diff --git a/ui/scss/_mediaelement.scss b/ui/scss/_mediaelement.scss
deleted file mode 100644
index 44488808f..000000000
--- a/ui/scss/_mediaelement.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-@import "global";
-
-.mejs-container, .mejs-overlay, .mejs-mediaelement {
- width: 100%;
- height: 100%;
-}
-
-.me-plugin {
- width: 100%;
- height: 100%;
-
- > embed {
- width: 100%;
- height: 100%;
- }
-}
\ No newline at end of file
diff --git a/ui/scss/all.scss b/ui/scss/all.scss
index 14833dfd3..6b421196a 100644
--- a/ui/scss/all.scss
+++ b/ui/scss/all.scss
@@ -1,6 +1,5 @@
@import "_reset";
@import "_icons";
-@import "_mediaelement";
@import "_gui";
@import "component/_table";
@import "component/_button.scss";
@@ -20,6 +19,5 @@
@import "component/_snack-bar.scss";
@import "component/_video.scss";
@import "page/_developer.scss";
-@import "page/_watch.scss";
@import "page/_reward.scss";
@import "page/_show.scss";
diff --git a/ui/scss/component/_video.scss b/ui/scss/component/_video.scss
index 9dd95ebe9..739dc1b26 100644
--- a/ui/scss/component/_video.scss
+++ b/ui/scss/component/_video.scss
@@ -3,6 +3,9 @@ video {
box-sizing: border-box;
max-height: 100%;
max-width: 100%;
+ background-size: contain;
+ background-position: center center;
+ background-repeat: no-repeat;
}
.video {
@@ -15,6 +18,7 @@ video {
max-width: $width-page-constrained;
max-height: $height-video-embedded;
height: $height-video-embedded;
+ position: relative; /*for .plyr below*/
video {
height: 100%;
}
@@ -24,6 +28,10 @@ video {
&.video--active {
/*background: none;*/
}
+ .plyr {
+ top: 50%;
+ transform: translateY(-50%);
+ }
}
.video__cover {
diff --git a/ui/scss/page/_watch.scss b/ui/scss/page/_watch.scss
deleted file mode 100644
index 6ed5459ae..000000000
--- a/ui/scss/page/_watch.scss
+++ /dev/null
@@ -1,48 +0,0 @@
-
-.video__overlay {
- position: absolute;
- top: 0px;
- left: 0px;
- color: #fff;
- z-index: 1;
-}
-
-.video__back {
- margin-top: 30px;
- margin-left: 50px;
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
-.video__back-link {
- font-size: 50px;
-}
-
-.video__back-label {
- opacity: 0.5;
- transition: opacity 100ms ease-in;
-}
-
-.video__back-link:hover + .video__back-label {
- opacity: 1;
-}
-
-$video-back-background: #333;
-$video-back-size: 20px;
-
-.video__back-label-arrow {
- color: $video-back-background;
- font-size: $video-back-size;
-}
-
-.video__back-label-content {
- display: inline-block;
- margin-left: -2px;
- font-size: $video-back-size;
- padding: $spacing-vertical / 2;
- border-radius: 3px;
- background-color: $video-back-background;
- color: #fff;
- pointer-events: none;
-}