Merge branch 'redux' into file-selector
This commit is contained in:
commit
f60d17f604
73 changed files with 1328 additions and 1503 deletions
|
@ -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
|
* Handle more of price calculations at the daemon layer to improve page load time
|
||||||
* Add special support for building channel claims in lbryuri module
|
* Add special support for building channel claims in lbryuri module
|
||||||
* Enable windows code signing of binary
|
* Enable windows code signing of binary
|
||||||
|
* Support for opening LBRY URIs from links in other apps
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -66,6 +67,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Fix Watch page and progress bars for new API changes
|
* 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
50
app/main.js
50
app/main.js
|
@ -37,6 +37,22 @@ let readyToQuit = false;
|
||||||
// send it to, it's cached in this variable.
|
// send it to, it's cached in this variable.
|
||||||
let openUri = null;
|
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 checkForNewVersion(callback) {
|
||||||
function formatRc(ver) {
|
function formatRc(ver) {
|
||||||
// Adds dash if needed to make RC suffix semver friendly
|
// Adds dash if needed to make RC suffix semver friendly
|
||||||
|
@ -186,6 +202,29 @@ function quitNow() {
|
||||||
app.quit();
|
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(){
|
app.on('ready', function(){
|
||||||
launchDaemonIfNotRunning();
|
launchDaemonIfNotRunning();
|
||||||
|
@ -324,6 +363,8 @@ function upgrade(event, installerPath) {
|
||||||
|
|
||||||
ipcMain.on('upgrade', upgrade);
|
ipcMain.on('upgrade', upgrade);
|
||||||
|
|
||||||
|
app.setAsDefaultProtocolClient('lbry');
|
||||||
|
|
||||||
if (process.platform == 'darwin') {
|
if (process.platform == 'darwin') {
|
||||||
app.on('open-url', (event, uri) => {
|
app.on('open-url', (event, uri) => {
|
||||||
if (!win) {
|
if (!win) {
|
||||||
|
@ -333,7 +374,10 @@ if (process.platform == 'darwin') {
|
||||||
win.webContents.send('open-uri-requested', uri);
|
win.webContents.send('open-uri-requested', uri);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (process.argv.length >= 3) {
|
} else if (process.argv.length >= 2) {
|
||||||
// No open-url event on Win, but we can still handle URIs provided at launch time
|
if (!win) {
|
||||||
win.webContents.send('open-uri-requested', process.argv[2]);
|
openUri = denormalizeUri(process.argv[1]);
|
||||||
|
} else {
|
||||||
|
win.webContents.send('open-uri-requested', denormalizeUri(process.argv[1]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
1
lbry
Submodule
1
lbry
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit d99fc519b56ee910a44ef4af668b0770e9430d12
|
1
lbryschema
Submodule
1
lbryschema
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5c2441fa13e39ba7280292519041e14ec696d753
|
1
lbryum
Submodule
1
lbryum
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 950b95aa7e45a2c15b269d807f6ff8e16bae4304
|
|
@ -6,7 +6,6 @@ import {
|
||||||
selectUpgradeDownloadItem,
|
selectUpgradeDownloadItem,
|
||||||
selectUpgradeFilename,
|
selectUpgradeFilename,
|
||||||
selectPageTitle,
|
selectPageTitle,
|
||||||
selectCurrentPath,
|
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
|
|
||||||
const {remote, ipcRenderer, shell} = require('electron');
|
const {remote, ipcRenderer, shell} = require('electron');
|
||||||
|
@ -32,8 +31,7 @@ export function doNavigate(path, params = {}) {
|
||||||
|
|
||||||
const state = getState()
|
const state = getState()
|
||||||
const pageTitle = selectPageTitle(state)
|
const pageTitle = selectPageTitle(state)
|
||||||
history.pushState(params, pageTitle, url)
|
dispatch(doHistoryPush(params, pageTitle, url))
|
||||||
window.document.title = pageTitle
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +43,6 @@ export function doChangePath(path) {
|
||||||
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) {
|
export function doOpenModal(modal) {
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
import * as types from 'constants/action_types'
|
import * as types from 'constants/action_types'
|
||||||
import lbry from 'lbry'
|
import lbry from 'lbry'
|
||||||
import {
|
import {
|
||||||
selectCurrentUri,
|
selectFetchingAvailability
|
||||||
} from 'selectors/app'
|
} from 'selectors/availability'
|
||||||
|
|
||||||
export function doFetchUriAvailability(uri) {
|
export function doFetchAvailability(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
const alreadyFetching = !!selectFetchingAvailability(state)[uri]
|
||||||
|
|
||||||
|
if (!alreadyFetching) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_AVAILABILITY_STARTED,
|
type: types.FETCH_AVAILABILITY_STARTED,
|
||||||
data: {uri}
|
data: {uri}
|
||||||
})
|
})
|
||||||
|
|
||||||
const successCallback = (availability) => {
|
lbry.get_availability({uri}).then((availability) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_AVAILABILITY_COMPLETED,
|
type: types.FETCH_AVAILABILITY_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
|
@ -19,21 +23,7 @@ export function doFetchUriAvailability(uri) {
|
||||||
uri,
|
uri,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
|
|
||||||
const errorCallback = () => {
|
|
||||||
console.debug('error')
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.get_availability({ uri }, successCallback, errorCallback)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doFetchCurrentUriAvailability() {
|
|
||||||
return function(dispatch, getState) {
|
|
||||||
const state = getState()
|
|
||||||
const uri = selectCurrentUri(state)
|
|
||||||
|
|
||||||
dispatch(doFetchUriAvailability(uri))
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -3,18 +3,18 @@ import lbry from 'lbry'
|
||||||
import lbryio from 'lbryio'
|
import lbryio from 'lbryio'
|
||||||
import lbryuri from 'lbryuri'
|
import lbryuri from 'lbryuri'
|
||||||
import rewards from 'rewards'
|
import rewards from 'rewards'
|
||||||
import {
|
|
||||||
selectCurrentUri,
|
|
||||||
} from 'selectors/app'
|
|
||||||
import {
|
import {
|
||||||
selectBalance,
|
selectBalance,
|
||||||
} from 'selectors/wallet'
|
} from 'selectors/wallet'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriFileInfo,
|
selectFileInfoForUri,
|
||||||
selectDownloadingByUri,
|
selectUrisDownloading,
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriCostInfo,
|
selectResolvingUris
|
||||||
|
} from 'selectors/content'
|
||||||
|
import {
|
||||||
|
selectCostInfoForUri,
|
||||||
} from 'selectors/cost_info'
|
} from 'selectors/cost_info'
|
||||||
import {
|
import {
|
||||||
selectClaimsByUri,
|
selectClaimsByUri,
|
||||||
|
@ -22,12 +22,14 @@ import {
|
||||||
import {
|
import {
|
||||||
doOpenModal,
|
doOpenModal,
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
import {
|
|
||||||
doFetchCostInfoForUri,
|
|
||||||
} from 'actions/cost_info'
|
|
||||||
|
|
||||||
export function doResolveUri(uri) {
|
export function doResolveUri(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
|
||||||
|
const state = getState()
|
||||||
|
const alreadyResolving = selectResolvingUris(state).indexOf(uri) !== -1
|
||||||
|
|
||||||
|
if (!alreadyResolving) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.RESOLVE_URI_STARTED,
|
type: types.RESOLVE_URI_STARTED,
|
||||||
data: { uri }
|
data: { uri }
|
||||||
|
@ -47,69 +49,17 @@ export function doResolveUri(uri) {
|
||||||
certificate,
|
certificate,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
dispatch(doFetchCostInfoForUri(uri))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function doFetchDownloadedContent() {
|
export function doCancelResolveUri(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState()
|
lbry.cancelResolve({ uri })
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_DOWNLOADED_CONTENT_STARTED,
|
type: types.RESOLVE_URI_CANCELED,
|
||||||
})
|
data: { uri }
|
||||||
|
|
||||||
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))
|
|
||||||
})
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: types.FETCH_DOWNLOADED_CONTENT_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)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,6 +81,14 @@ export function doFetchFeaturedUris() {
|
||||||
featuredUris[category] = Uris[category]
|
featuredUris[category] = Uris[category]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
//
|
||||||
|
// dispatch({
|
||||||
|
// type: types.FETCH_FEATURED_CONTENT_COMPLETED,
|
||||||
|
// data: {
|
||||||
|
// categories: ["FOO"],
|
||||||
|
// uris: { FOO: ["lbry://gtasoc"]},
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_FEATURED_CONTENT_COMPLETED,
|
type: types.FETCH_FEATURED_CONTENT_COMPLETED,
|
||||||
|
@ -175,6 +133,7 @@ export function doUpdateLoadStatus(uri, outpoint) {
|
||||||
type: types.DOWNLOADING_COMPLETED,
|
type: types.DOWNLOADING_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
uri,
|
uri,
|
||||||
|
outpoint,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -190,6 +149,7 @@ export function doUpdateLoadStatus(uri, outpoint) {
|
||||||
type: types.DOWNLOADING_PROGRESSED,
|
type: types.DOWNLOADING_PROGRESSED,
|
||||||
data: {
|
data: {
|
||||||
uri,
|
uri,
|
||||||
|
outpoint,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
progress,
|
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) {
|
export function doDownloadFile(uri, streamInfo) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState()
|
const state = getState()
|
||||||
|
@ -216,6 +169,7 @@ export function doDownloadFile(uri, streamInfo) {
|
||||||
type: types.DOWNLOADING_STARTED,
|
type: types.DOWNLOADING_STARTED,
|
||||||
data: {
|
data: {
|
||||||
uri,
|
uri,
|
||||||
|
outpoint: streamInfo.outpoint,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -230,10 +184,9 @@ export function doDownloadFile(uri, streamInfo) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doLoadVideo() {
|
export function doLoadVideo(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState()
|
const state = getState()
|
||||||
const uri = selectCurrentUri(state)
|
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.LOADING_VIDEO_STARTED,
|
type: types.LOADING_VIDEO_STARTED,
|
||||||
|
@ -260,14 +213,13 @@ export function doLoadVideo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doWatchVideo() {
|
export function doPurchaseUri(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState()
|
const state = getState()
|
||||||
const uri = selectCurrentUri(state)
|
|
||||||
const balance = selectBalance(state)
|
const balance = selectBalance(state)
|
||||||
const fileInfo = selectCurrentUriFileInfo(state)
|
const fileInfo = selectFileInfoForUri(state, { uri })
|
||||||
const costInfo = selectCurrentUriCostInfo(state)
|
const costInfo = selectCostInfoForUri(state, { uri })
|
||||||
const downloadingByUri = selectDownloadingByUri(state)
|
const downloadingByUri = selectUrisDownloading(state)
|
||||||
const alreadyDownloading = !!downloadingByUri[uri]
|
const alreadyDownloading = !!downloadingByUri[uri]
|
||||||
const { cost } = costInfo
|
const { cost } = costInfo
|
||||||
|
|
||||||
|
@ -288,8 +240,8 @@ export function doWatchVideo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// the file is free or we have partially downloaded it
|
// the file is free or we have partially downloaded it
|
||||||
if (cost <= 0.01 || fileInfo.download_directory) {
|
if (cost <= 0.01 || (fileInfo && fileInfo.download_directory)) {
|
||||||
dispatch(doLoadVideo())
|
dispatch(doLoadVideo(uri))
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,3 +254,44 @@ export function doWatchVideo() {
|
||||||
return Promise.resolve()
|
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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,51 @@
|
||||||
import * as types from 'constants/action_types'
|
import * as types from 'constants/action_types'
|
||||||
import {
|
|
||||||
selectCurrentUri,
|
|
||||||
} from 'selectors/app'
|
|
||||||
import lbry from 'lbry'
|
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) {
|
export function doFetchCostInfoForUri(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
const state = getState(),
|
||||||
|
claim = selectClaimsByUri(state)[uri],
|
||||||
|
isResolving = selectResolvingUris(state).indexOf(uri) !== -1,
|
||||||
|
isGenerous = selectSettingsIsGenerous(state)
|
||||||
|
|
||||||
|
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({
|
dispatch({
|
||||||
type: types.FETCH_COST_INFO_STARTED,
|
type: types.FETCH_COST_INFO_STARTED,
|
||||||
data: {
|
data: {
|
||||||
uri,
|
uri,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
lbry.getCostInfo(uri).then(costInfo => {
|
function resolve(costInfo) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_COST_INFO_COMPLETED,
|
type: types.FETCH_COST_INFO_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
|
@ -21,15 +53,25 @@ export function doFetchCostInfoForUri(uri) {
|
||||||
costInfo,
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
import * as types from 'constants/action_types'
|
import * as types from 'constants/action_types'
|
||||||
import lbry from 'lbry'
|
import lbry from 'lbry'
|
||||||
import {
|
import {
|
||||||
selectCurrentUri,
|
doClaimListMine
|
||||||
} from 'selectors/app'
|
} from 'actions/content'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriClaimOutpoint,
|
selectClaimsByUri,
|
||||||
|
selectClaimListMineIsPending,
|
||||||
} from 'selectors/claims'
|
} from 'selectors/claims'
|
||||||
|
import {
|
||||||
|
selectFileListIsPending,
|
||||||
|
selectAllFileInfos,
|
||||||
|
selectUrisLoading,
|
||||||
|
} from 'selectors/file_info'
|
||||||
import {
|
import {
|
||||||
doCloseModal,
|
doCloseModal,
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
|
@ -14,31 +20,56 @@ const {
|
||||||
shell,
|
shell,
|
||||||
} = require('electron')
|
} = require('electron')
|
||||||
|
|
||||||
export function doFetchCurrentUriFileInfo() {
|
export function doFetchFileInfo(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState()
|
const state = getState()
|
||||||
const uri = selectCurrentUri(state)
|
const claim = selectClaimsByUri(state)[uri]
|
||||||
const outpoint = selectCurrentUriClaimOutpoint(state)
|
const outpoint = claim ? `${claim.txid}:${claim.nout}` : null
|
||||||
|
const alreadyFetching = !!selectUrisLoading(state)[uri]
|
||||||
|
|
||||||
|
if (!alreadyFetching) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_FILE_INFO_STARTED,
|
type: types.FETCH_FILE_INFO_STARTED,
|
||||||
data: {
|
data: {
|
||||||
uri,
|
|
||||||
outpoint,
|
outpoint,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
lbry.file_list({ outpoint: outpoint, full_status: true }).then(([fileInfo]) => {
|
lbry.file_list({outpoint: outpoint, full_status: true}).then(fileInfos => {
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_FILE_INFO_COMPLETED,
|
type: types.FETCH_FILE_INFO_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
uri,
|
outpoint,
|
||||||
fileInfo,
|
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,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function doOpenFileInShell(fileInfo) {
|
export function doOpenFileInShell(fileInfo) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
@ -52,51 +83,39 @@ export function doOpenFileInFolder(fileInfo) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doDeleteFile(uri, fileInfo, deleteFromComputer) {
|
export function doDeleteFile(outpoint, deleteFromComputer) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.DELETE_FILE_STARTED,
|
type: types.FILE_DELETE,
|
||||||
data: {
|
data: {
|
||||||
uri,
|
outpoint
|
||||||
fileInfo,
|
|
||||||
deleteFromComputer,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const successCallback = () => {
|
lbry.file_delete({
|
||||||
dispatch({
|
outpoint: outpoint,
|
||||||
type: types.DELETE_FILE_COMPLETED,
|
delete_target_file: deleteFromComputer,
|
||||||
data: {
|
|
||||||
uri,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
dispatch(doCloseModal())
|
dispatch(doCloseModal())
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.removeFile(fileInfo.outpoint, deleteFromComputer, successCallback)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doFetchDownloadedContent() {
|
|
||||||
|
export function doFetchFileInfosAndPublishedClaims() {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState()
|
const state = getState(),
|
||||||
|
isClaimListMinePending = selectClaimListMineIsPending(state),
|
||||||
|
isFileInfoListPending = selectFileListIsPending(state)
|
||||||
|
|
||||||
dispatch({
|
if (isClaimListMinePending === undefined) {
|
||||||
type: types.FETCH_DOWNLOADED_CONTENT_STARTED,
|
dispatch(doClaimListMine())
|
||||||
})
|
}
|
||||||
|
|
||||||
lbry.claim_list_mine().then((myClaimInfos) => {
|
if (isFileInfoListPending === undefined) {
|
||||||
lbry.file_list().then((fileInfos) => {
|
dispatch(doFileList())
|
||||||
const myClaimOutpoints = myClaimInfos.map(({txid, nout}) => txid + ':' + nout);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: types.FETCH_DOWNLOADED_CONTENT_COMPLETED,
|
|
||||||
data: {
|
|
||||||
fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)),
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
} from 'actions/content'
|
} from 'actions/content'
|
||||||
import {
|
import {
|
||||||
doNavigate,
|
doNavigate,
|
||||||
|
doHistoryPush
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
import {
|
import {
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
|
@ -29,6 +30,8 @@ export function doSearch(query) {
|
||||||
|
|
||||||
if(page != 'search') {
|
if(page != 'search') {
|
||||||
dispatch(doNavigate('search', { query: query }))
|
dispatch(doNavigate('search', { query: query }))
|
||||||
|
} else {
|
||||||
|
dispatch(doHistoryPush({ query }, "Search for " + query, '/search'))
|
||||||
}
|
}
|
||||||
|
|
||||||
lighthouse.search(query).then(results => {
|
lighthouse.search(query).then(results => {
|
||||||
|
|
31
ui/js/actions/settings.js
Normal file
31
ui/js/actions/settings.js
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,30 +2,23 @@ import React from 'react';
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
selectCurrentPage,
|
|
||||||
selectCurrentModal,
|
selectCurrentModal,
|
||||||
selectDrawerOpen,
|
|
||||||
selectHeaderLinks,
|
|
||||||
selectSearchTerm,
|
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
import {
|
import {
|
||||||
doCheckUpgradeAvailable,
|
doCheckUpgradeAvailable,
|
||||||
doOpenModal,
|
|
||||||
doCloseModal,
|
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
doUpdateBalance,
|
||||||
|
} from 'actions/wallet'
|
||||||
import App from './view'
|
import App from './view'
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
currentPage: selectCurrentPage(state),
|
|
||||||
modal: selectCurrentModal(state),
|
modal: selectCurrentModal(state),
|
||||||
headerLinks: selectHeaderLinks(state),
|
|
||||||
searchTerm: selectSearchTerm(state)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
||||||
openModal: () => dispatch(doOpenModal()),
|
updateBalance: (balance) => dispatch(doUpdateBalance(balance))
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(select, perform)(App)
|
export default connect(select, perform)(App)
|
||||||
|
|
|
@ -4,7 +4,8 @@ import Header from 'component/header';
|
||||||
import ErrorModal from 'component/errorModal'
|
import ErrorModal from 'component/errorModal'
|
||||||
import DownloadingModal from 'component/downloadingModal'
|
import DownloadingModal from 'component/downloadingModal'
|
||||||
import UpgradeModal from 'component/upgradeModal'
|
import UpgradeModal from 'component/upgradeModal'
|
||||||
import {Line} from 'rc-progress';
|
import lbry from 'lbry'
|
||||||
|
import {Line} from 'rc-progress'
|
||||||
|
|
||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
@ -15,12 +16,15 @@ class App extends React.Component {
|
||||||
if (!this.props.upgradeSkipped) {
|
if (!this.props.upgradeSkipped) {
|
||||||
this.props.checkUpgradeAvailable()
|
this.props.checkUpgradeAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lbry.balanceSubscribe((balance) => {
|
||||||
|
this.props.updateBalance(balance)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
modal,
|
modal,
|
||||||
headerLinks,
|
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return <div id="window">
|
return <div id="window">
|
||||||
|
|
|
@ -200,7 +200,7 @@ const CodeRequiredStage = React.createClass({
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!this.state.address) {
|
if (!this.state.address) {
|
||||||
lbry.getUnusedAddress((address) => {
|
lbry.wallet_unused_address().then((address) => {
|
||||||
setLocal('wallet_address', address);
|
setLocal('wallet_address', address);
|
||||||
this.setState({ address: address });
|
this.setState({ address: address });
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,9 +3,6 @@ import {
|
||||||
connect,
|
connect,
|
||||||
} from 'react-redux'
|
} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
selectObscureNsfw,
|
|
||||||
selectHidePrice,
|
|
||||||
selectHasSignature,
|
|
||||||
selectPlatform,
|
selectPlatform,
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
import {
|
import {
|
||||||
|
@ -14,7 +11,7 @@ import {
|
||||||
makeSelectLoadingForUri,
|
makeSelectLoadingForUri,
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
import {
|
import {
|
||||||
makeSelectAvailabilityForUri,
|
makeSelectIsAvailableForUri,
|
||||||
} from 'selectors/availability'
|
} from 'selectors/availability'
|
||||||
import {
|
import {
|
||||||
selectCurrentModal,
|
selectCurrentModal,
|
||||||
|
@ -22,47 +19,50 @@ import {
|
||||||
import {
|
import {
|
||||||
doCloseModal,
|
doCloseModal,
|
||||||
doOpenModal,
|
doOpenModal,
|
||||||
|
doHistoryBack,
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
doFetchAvailability
|
||||||
|
} from 'actions/availability'
|
||||||
import {
|
import {
|
||||||
doOpenFileInShell,
|
doOpenFileInShell,
|
||||||
doOpenFileInFolder,
|
doOpenFileInFolder,
|
||||||
doDeleteFile,
|
doDeleteFile,
|
||||||
} from 'actions/file_info'
|
} from 'actions/file_info'
|
||||||
import {
|
import {
|
||||||
doWatchVideo,
|
doPurchaseUri,
|
||||||
doLoadVideo,
|
doLoadVideo,
|
||||||
} from 'actions/content'
|
} from 'actions/content'
|
||||||
import FileActions from './view'
|
import FileActions from './view'
|
||||||
|
|
||||||
const makeSelect = () => {
|
const makeSelect = () => {
|
||||||
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
||||||
const selectAvailabilityForUri = makeSelectAvailabilityForUri()
|
const selectIsAvailableForUri = makeSelectIsAvailableForUri()
|
||||||
const selectDownloadingForUri = makeSelectDownloadingForUri()
|
const selectDownloadingForUri = makeSelectDownloadingForUri()
|
||||||
const selectLoadingForUri = makeSelectLoadingForUri()
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
obscureNsfw: selectObscureNsfw(state),
|
|
||||||
hidePrice: selectHidePrice(state),
|
|
||||||
hasSignature: selectHasSignature(state),
|
|
||||||
fileInfo: selectFileInfoForUri(state, props),
|
fileInfo: selectFileInfoForUri(state, props),
|
||||||
availability: selectAvailabilityForUri(state, props),
|
isAvailable: selectIsAvailableForUri(state, props),
|
||||||
platform: selectPlatform(state),
|
platform: selectPlatform(state),
|
||||||
modal: selectCurrentModal(state),
|
modal: selectCurrentModal(state),
|
||||||
downloading: selectDownloadingForUri(state, props),
|
downloading: selectDownloadingForUri(state, props),
|
||||||
loading: selectLoadingForUri(state, props),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return select
|
return select
|
||||||
}
|
}
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
|
checkAvailability: (uri) => dispatch(doFetchAvailability(uri)),
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
openInFolder: (fileInfo) => dispatch(doOpenFileInFolder(fileInfo)),
|
openInFolder: (fileInfo) => dispatch(doOpenFileInFolder(fileInfo)),
|
||||||
openInShell: (fileInfo) => dispatch(doOpenFileInShell(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)),
|
openModal: (modal) => dispatch(doOpenModal(modal)),
|
||||||
downloadClick: () => dispatch(doWatchVideo()),
|
startDownload: (uri) => dispatch(doPurchaseUri(uri)),
|
||||||
loadVideo: () => dispatch(doLoadVideo())
|
loadVideo: (uri) => dispatch(doLoadVideo(uri))
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(FileActions)
|
export default connect(makeSelect, perform)(FileActions)
|
|
@ -1,7 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from 'lbry';
|
import {Icon,BusyMessage} from 'component/common';
|
||||||
import lbryuri from 'lbryuri';
|
|
||||||
import {Icon,} from 'component/common';
|
|
||||||
import FilePrice from 'component/filePrice'
|
import FilePrice from 'component/filePrice'
|
||||||
import {Modal} from 'component/modal';
|
import {Modal} from 'component/modal';
|
||||||
import {FormField} from 'component/form';
|
import {FormField} from 'component/form';
|
||||||
|
@ -9,14 +7,36 @@ import Link from 'component/link';
|
||||||
import {ToolTip} from 'component/tooltip';
|
import {ToolTip} from 'component/tooltip';
|
||||||
import {DropDownMenu, DropDownMenuItem} from 'component/menu';
|
import {DropDownMenu, DropDownMenuItem} from 'component/menu';
|
||||||
|
|
||||||
class FileActionsRow extends React.Component {
|
class FileActions extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
|
forceShowActions: false,
|
||||||
deleteChecked: 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) {
|
handleDeleteCheckboxClicked(event) {
|
||||||
this.setState({
|
this.setState({
|
||||||
deleteChecked: event.target.checked,
|
deleteChecked: event.target.checked,
|
||||||
|
@ -25,65 +45,73 @@ class FileActionsRow extends React.Component {
|
||||||
|
|
||||||
onAffirmPurchase() {
|
onAffirmPurchase() {
|
||||||
this.props.closeModal()
|
this.props.closeModal()
|
||||||
this.props.loadVideo()
|
this.props.loadVideo(this.props.uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
fileInfo,
|
fileInfo,
|
||||||
|
isAvailable,
|
||||||
platform,
|
platform,
|
||||||
downloading,
|
downloading,
|
||||||
loading,
|
|
||||||
uri,
|
uri,
|
||||||
deleteFile,
|
deleteFile,
|
||||||
openInFolder,
|
openInFolder,
|
||||||
openInShell,
|
openInShell,
|
||||||
modal,
|
modal,
|
||||||
openModal,
|
openModal,
|
||||||
affirmPurchase,
|
|
||||||
closeModal,
|
closeModal,
|
||||||
downloadClick,
|
startDownload,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const {
|
const deleteChecked = this.state.deleteChecked,
|
||||||
deleteChecked,
|
metadata = fileInfo ? fileInfo.metadata : null,
|
||||||
} = this.state
|
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)
|
if (downloading) {
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = <Link button="text" label="Download" icon="icon-download" onClick={downloadClick} />;
|
|
||||||
} else if (downloading || loading) {
|
|
||||||
const
|
const
|
||||||
progress = (fileInfo && fileInfo.written_bytes) ? fileInfo.written_bytes / fileInfo.total_bytes * 100 : 0,
|
progress = (fileInfo && fileInfo.written_bytes) ? fileInfo.written_bytes / fileInfo.total_bytes * 100 : 0,
|
||||||
label = fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...',
|
label = fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...',
|
||||||
labelWithIcon = <span className="button__content"><Icon icon="icon-download" /><span>{label}</span></span>;
|
labelWithIcon = <span className="button__content"><Icon icon="icon-download" /><span>{label}</span></span>;
|
||||||
|
|
||||||
linkBlock = (
|
content = <div className="faux-button-block file-actions__download-status-bar button-set-item">
|
||||||
<div className="faux-button-block file-actions__download-status-bar button-set-item">
|
|
||||||
<div className="faux-button-block file-actions__download-status-bar-overlay" style={{ width: progress + '%' }}>{labelWithIcon}</div>
|
<div className="faux-button-block file-actions__download-status-bar-overlay" style={{ width: progress + '%' }}>{labelWithIcon}</div>
|
||||||
{labelWithIcon}
|
{labelWithIcon}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
} else if (!fileInfo && isAvailable === undefined) {
|
||||||
|
|
||||||
|
content = <BusyMessage message="Checking availability" />
|
||||||
|
|
||||||
|
} else if (!fileInfo && !isAvailable && !this.state.forceShowActions) {
|
||||||
|
|
||||||
|
content = <div>
|
||||||
|
<div className="button-set-item empty">Content unavailable.</div>
|
||||||
|
<ToolTip label="Why?"
|
||||||
|
body="The content on LBRY is hosted by its users. It appears there are no users connected that have this file at the moment."
|
||||||
|
className="button-set-item" />
|
||||||
|
<Link label="Try Anyway" onClick={this.onShowFileActionsRowClicked.bind(this)} className="button-text button-set-item" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
} else if (fileInfo === null && !downloading) {
|
||||||
|
|
||||||
|
content = <Link button="text" label="Download" icon="icon-download" onClick={() => { startDownload(uri) } } />;
|
||||||
|
|
||||||
|
} else if (fileInfo && fileInfo.download_path) {
|
||||||
|
content = <Link label="Open" button="text" icon="icon-folder-open" onClick={() => openInShell(fileInfo)} />;
|
||||||
} else {
|
} else {
|
||||||
linkBlock = <Link label="Open" button="text" icon="icon-folder-open" onClick={() => openInShell(fileInfo)} />;
|
console.log('handle this case of file action props?');
|
||||||
|
console.log(this.props)
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = metadata ? metadata.title : uri;
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<section className="file-actions">
|
||||||
{fileInfo !== null || fileInfo.isMine
|
{ content }
|
||||||
? linkBlock
|
|
||||||
: null}
|
|
||||||
{ showMenu ?
|
{ showMenu ?
|
||||||
<DropDownMenu>
|
<DropDownMenu>
|
||||||
<DropDownMenuItem key={0} onClick={() => openInFolder(fileInfo)} label={openInFolderMessage} />
|
<DropDownMenuItem key={0} onClick={() => openInFolder(fileInfo)} label={openInFolderMessage} />
|
||||||
|
@ -91,7 +119,7 @@ class FileActionsRow extends React.Component {
|
||||||
</DropDownMenu> : '' }
|
</DropDownMenu> : '' }
|
||||||
<Modal type="confirm" isOpen={modal == 'affirmPurchase'}
|
<Modal type="confirm" isOpen={modal == 'affirmPurchase'}
|
||||||
contentLabel="Confirm Purchase" onConfirmed={this.onAffirmPurchase.bind(this)} onAborted={closeModal}>
|
contentLabel="Confirm Purchase" onConfirmed={this.onAffirmPurchase.bind(this)} onAborted={closeModal}>
|
||||||
Are you sure you'd like to buy <strong>{title}</strong> for <strong><FilePrice uri={uri} look="plain" /></strong> credits?
|
This will purchase <strong>{title}</strong> for <strong><FilePrice uri={uri} look="plain" /></strong> credits.
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal isOpen={modal == 'notEnoughCredits'} contentLabel="Not enough credits"
|
<Modal isOpen={modal == 'notEnoughCredits'} contentLabel="Not enough credits"
|
||||||
onConfirmed={closeModal}>
|
onConfirmed={closeModal}>
|
||||||
|
@ -105,59 +133,15 @@ class FileActionsRow extends React.Component {
|
||||||
contentLabel="Not enough credits"
|
contentLabel="Not enough credits"
|
||||||
type="confirm"
|
type="confirm"
|
||||||
confirmButtonLabel="Remove"
|
confirmButtonLabel="Remove"
|
||||||
onConfirmed={() => deleteFile(uri, fileInfo, deleteChecked)}
|
onConfirmed={() => deleteFile(fileInfo.outpoint, deleteChecked)}
|
||||||
onAborted={closeModal}>
|
onAborted={closeModal}>
|
||||||
<p>Are you sure you'd like to remove <cite>{title}</cite> from LBRY?</p>
|
<p>Are you sure you'd like to remove <cite>{title}</cite> from LBRY?</p>
|
||||||
|
|
||||||
<label><FormField type="checkbox" checked={deleteChecked} onClick={this.handleDeleteCheckboxClicked.bind(this)} /> Delete this file from my computer</label>
|
<label><FormField type="checkbox" checked={deleteChecked} onClick={this.handleDeleteCheckboxClicked.bind(this)} /> Delete this file from my computer</label>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (<section className="file-actions">
|
|
||||||
{
|
|
||||||
fileInfo || this.state.available || this.state.forceShowActions
|
|
||||||
? <FileActionsRow {...this.props} />
|
|
||||||
: <div>
|
|
||||||
<div className="button-set-item empty">Content unavailable.</div>
|
|
||||||
<ToolTip label="Why?"
|
|
||||||
body="The content on LBRY is hosted by its users. It appears there are no users connected that have this file at the moment."
|
|
||||||
className="button-set-item" />
|
|
||||||
<Link label="Try Anyway" onClick={this.onShowFileActionsRowClicked.bind(this)} className="button-text button-set-item" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</section>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FileActions
|
export default FileActions
|
||||||
|
|
|
@ -7,21 +7,20 @@ import {
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
import {
|
import {
|
||||||
doResolveUri,
|
doResolveUri,
|
||||||
|
doCancelResolveUri,
|
||||||
} from 'actions/content'
|
} from 'actions/content'
|
||||||
import {
|
import {
|
||||||
selectHidePrice,
|
|
||||||
selectObscureNsfw,
|
selectObscureNsfw,
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
import {
|
import {
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectSourceForUri,
|
|
||||||
makeSelectMetadataForUri,
|
makeSelectMetadataForUri,
|
||||||
} from 'selectors/claims'
|
} from 'selectors/claims'
|
||||||
import {
|
import {
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
import {
|
import {
|
||||||
makeSelectResolvingUri,
|
makeSelectIsResolvingForUri,
|
||||||
} from 'selectors/content'
|
} from 'selectors/content'
|
||||||
import FileCard from './view'
|
import FileCard from './view'
|
||||||
|
|
||||||
|
@ -29,17 +28,13 @@ const makeSelect = () => {
|
||||||
const selectClaimForUri = makeSelectClaimForUri()
|
const selectClaimForUri = makeSelectClaimForUri()
|
||||||
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
||||||
const selectMetadataForUri = makeSelectMetadataForUri()
|
const selectMetadataForUri = makeSelectMetadataForUri()
|
||||||
const selectSourceForUri = makeSelectSourceForUri()
|
const selectResolvingUri = makeSelectIsResolvingForUri()
|
||||||
const selectResolvingUri = makeSelectResolvingUri()
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: selectClaimForUri(state, props),
|
claim: selectClaimForUri(state, props),
|
||||||
fileInfo: selectFileInfoForUri(state, props),
|
fileInfo: selectFileInfoForUri(state, props),
|
||||||
hidePrice: selectHidePrice(state),
|
|
||||||
obscureNsfw: selectObscureNsfw(state),
|
obscureNsfw: selectObscureNsfw(state),
|
||||||
hasSignature: false,
|
|
||||||
metadata: selectMetadataForUri(state, props),
|
metadata: selectMetadataForUri(state, props),
|
||||||
source: selectSourceForUri(state, props),
|
|
||||||
isResolvingUri: selectResolvingUri(state, props),
|
isResolvingUri: selectResolvingUri(state, props),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -49,6 +44,7 @@ const makeSelect = () => {
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
resolveUri: (uri) => dispatch(doResolveUri(uri)),
|
resolveUri: (uri) => dispatch(doResolveUri(uri)),
|
||||||
|
cancelResolveUri: (uri) => dispatch(doCancelResolveUri(uri))
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(FileCard)
|
export default connect(makeSelect, perform)(FileCard)
|
||||||
|
|
|
@ -2,21 +2,41 @@ import React from 'react';
|
||||||
import lbry from 'lbry.js';
|
import lbry from 'lbry.js';
|
||||||
import lbryuri from 'lbryuri.js';
|
import lbryuri from 'lbryuri.js';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import {Thumbnail, TruncatedText,} from 'component/common';
|
import {Thumbnail, TruncatedText, Icon} from 'component/common';
|
||||||
import FilePrice from 'component/filePrice'
|
import FilePrice from 'component/filePrice'
|
||||||
import UriIndicator from 'component/uriIndicator';
|
import UriIndicator from 'component/uriIndicator';
|
||||||
|
|
||||||
class FileCard extends React.Component {
|
class FileCard extends React.Component {
|
||||||
componentDidMount() {
|
componentWillMount() {
|
||||||
|
this.resolve(this.props)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.resolve(nextProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(props) {
|
||||||
const {
|
const {
|
||||||
isResolvingUri,
|
isResolvingUri,
|
||||||
resolveUri,
|
resolveUri,
|
||||||
claim,
|
claim,
|
||||||
uri,
|
uri,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
if(!isResolvingUri && claim === undefined && uri) {
|
||||||
|
resolveUri(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const {
|
||||||
|
isResolvingUri,
|
||||||
|
cancelResolveUri,
|
||||||
|
uri
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
if(!isResolvingUri && !claim && uri) {
|
if (isResolvingUri) {
|
||||||
resolveUri(uri)
|
cancelResolveUri(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,10 +55,11 @@ class FileCard extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
claim,
|
||||||
|
fileInfo,
|
||||||
metadata,
|
metadata,
|
||||||
isResolvingUri,
|
isResolvingUri,
|
||||||
navigate,
|
navigate,
|
||||||
hidePrice,
|
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const uri = lbryuri.normalize(this.props.uri);
|
const uri = lbryuri.normalize(this.props.uri);
|
||||||
|
@ -50,6 +71,8 @@ class FileCard extends React.Component {
|
||||||
description = "Loading..."
|
description = "Loading..."
|
||||||
} else if (metadata && metadata.description) {
|
} else if (metadata && metadata.description) {
|
||||||
description = metadata.description
|
description = metadata.description
|
||||||
|
} else if (claim === null) {
|
||||||
|
description = 'This address contains no content.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -59,7 +82,10 @@ class FileCard extends React.Component {
|
||||||
<div className="card__title-identity">
|
<div className="card__title-identity">
|
||||||
<h5 title={title}><TruncatedText lines={1}>{title}</TruncatedText></h5>
|
<h5 title={title}><TruncatedText lines={1}>{title}</TruncatedText></h5>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
{ !hidePrice ? <span style={{float: "right"}}><FilePrice uri={uri} /></span> : null}
|
<span style={{float: "right"}}>
|
||||||
|
<FilePrice uri={uri} />
|
||||||
|
{ fileInfo ? <span>{' '}<Icon fixed icon="icon-folder" /></span> : '' }
|
||||||
|
</span>
|
||||||
<UriIndicator uri={uri} />
|
<UriIndicator uri={uri} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -58,8 +58,8 @@ class FileList extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
handleSortChanged,
|
handleSortChanged,
|
||||||
|
fetching,
|
||||||
fileInfos,
|
fileInfos,
|
||||||
hidePrices,
|
|
||||||
} = this.props
|
} = this.props
|
||||||
const {
|
const {
|
||||||
sortBy,
|
sortBy,
|
||||||
|
@ -71,10 +71,11 @@ class FileList extends React.Component {
|
||||||
contentName: fileInfo.name,
|
contentName: fileInfo.name,
|
||||||
channelName: fileInfo.channel_name,
|
channelName: fileInfo.channel_name,
|
||||||
})
|
})
|
||||||
content.push(<FileTile key={uri} uri={uri} hidePrice={hidePrices} hideOnRemove={true} showEmpty={""} />)
|
content.push(<FileTile key={uri} uri={uri} hidePrice={true} showEmpty={this.props.fileTileShowEmpty} />)
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<section>
|
<section className="file-list__header">
|
||||||
|
{ fetching && <span className="busy-indicator"/> }
|
||||||
<span className='sort-section'>
|
<span className='sort-section'>
|
||||||
Sort by { ' ' }
|
Sort by { ' ' }
|
||||||
<FormField type="select" onChange={this.handleSortChanged.bind(this)}>
|
<FormField type="select" onChange={this.handleSortChanged.bind(this)}>
|
||||||
|
|
|
@ -2,6 +2,9 @@ import React from 'react'
|
||||||
import {
|
import {
|
||||||
connect,
|
connect,
|
||||||
} from 'react-redux'
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
doFetchCostInfoForUri,
|
||||||
|
} from 'actions/cost_info'
|
||||||
import {
|
import {
|
||||||
makeSelectCostInfoForUri,
|
makeSelectCostInfoForUri,
|
||||||
} from 'selectors/cost_info'
|
} from 'selectors/cost_info'
|
||||||
|
@ -9,6 +12,7 @@ import FilePrice from './view'
|
||||||
|
|
||||||
const makeSelect = () => {
|
const makeSelect = () => {
|
||||||
const selectCostInfoForUri = makeSelectCostInfoForUri()
|
const selectCostInfoForUri = makeSelectCostInfoForUri()
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
costInfo: selectCostInfoForUri(state, props),
|
costInfo: selectCostInfoForUri(state, props),
|
||||||
})
|
})
|
||||||
|
@ -17,6 +21,8 @@ const makeSelect = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
|
fetchCostInfo: (uri) => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
|
// cancelFetchCostInfo: (uri) => dispatch(doCancelFetchCostInfoForUri(uri))
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(FilePrice)
|
export default connect(makeSelect, perform)(FilePrice)
|
||||||
|
|
|
@ -3,11 +3,32 @@ import {
|
||||||
CreditAmount,
|
CreditAmount,
|
||||||
} from 'component/common'
|
} from 'component/common'
|
||||||
|
|
||||||
const FilePrice = (props) => {
|
class FilePrice extends React.Component{
|
||||||
|
componentWillMount() {
|
||||||
|
this.fetchCost(this.props)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.fetchCost(nextProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchCost(props) {
|
||||||
|
const {
|
||||||
|
costInfo,
|
||||||
|
fetchCostInfo,
|
||||||
|
uri
|
||||||
|
} = props
|
||||||
|
|
||||||
|
if (costInfo === undefined) {
|
||||||
|
fetchCostInfo(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
const {
|
const {
|
||||||
costInfo,
|
costInfo,
|
||||||
look = 'indicator',
|
look = 'indicator',
|
||||||
} = props
|
} = this.props
|
||||||
|
|
||||||
const isEstimate = costInfo ? !costInfo.includesData : null
|
const isEstimate = costInfo ? !costInfo.includesData : null
|
||||||
|
|
||||||
|
@ -17,5 +38,6 @@ const FilePrice = (props) => {
|
||||||
|
|
||||||
return <CreditAmount label={false} amount={costInfo.cost} isEstimate={isEstimate} showFree={true} />
|
return <CreditAmount label={false} amount={costInfo.cost} isEstimate={isEstimate} showFree={true} />
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default FilePrice
|
export default FilePrice
|
||||||
|
|
|
@ -10,41 +10,30 @@ import {
|
||||||
} from 'actions/content'
|
} from 'actions/content'
|
||||||
import {
|
import {
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectSourceForUri,
|
|
||||||
makeSelectMetadataForUri,
|
makeSelectMetadataForUri,
|
||||||
} from 'selectors/claims'
|
} from 'selectors/claims'
|
||||||
import {
|
import {
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
import {
|
|
||||||
makeSelectFetchingAvailabilityForUri,
|
|
||||||
makeSelectAvailabilityForUri,
|
|
||||||
} from 'selectors/availability'
|
|
||||||
import {
|
import {
|
||||||
selectObscureNsfw,
|
selectObscureNsfw,
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
import {
|
import {
|
||||||
makeSelectResolvingUri,
|
makeSelectIsResolvingForUri,
|
||||||
} from 'selectors/content'
|
} from 'selectors/content'
|
||||||
import FileTile from './view'
|
import FileTile from './view'
|
||||||
|
|
||||||
const makeSelect = () => {
|
const makeSelect = () => {
|
||||||
const selectClaimForUri = makeSelectClaimForUri()
|
const selectClaimForUri = makeSelectClaimForUri()
|
||||||
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
||||||
const selectFetchingAvailabilityForUri = makeSelectFetchingAvailabilityForUri()
|
|
||||||
const selectAvailabilityForUri = makeSelectAvailabilityForUri()
|
|
||||||
const selectMetadataForUri = makeSelectMetadataForUri()
|
const selectMetadataForUri = makeSelectMetadataForUri()
|
||||||
const selectSourceForUri = makeSelectSourceForUri()
|
const selectResolvingUri = makeSelectIsResolvingForUri()
|
||||||
const selectResolvingUri = makeSelectResolvingUri()
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: selectClaimForUri(state, props),
|
claim: selectClaimForUri(state, props),
|
||||||
fileInfo: selectFileInfoForUri(state, props),
|
fileInfo: selectFileInfoForUri(state, props),
|
||||||
fetchingAvailability: selectFetchingAvailabilityForUri(state, props),
|
|
||||||
selectAvailabilityForUri: selectAvailabilityForUri(state, props),
|
|
||||||
obscureNsfw: selectObscureNsfw(state),
|
obscureNsfw: selectObscureNsfw(state),
|
||||||
metadata: selectMetadataForUri(state, props),
|
metadata: selectMetadataForUri(state, props),
|
||||||
source: selectSourceForUri(state, props),
|
|
||||||
isResolvingUri: selectResolvingUri(state, props),
|
isResolvingUri: selectResolvingUri(state, props),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,8 @@ class FileTile extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this._fileInfoSubscribeId = null
|
|
||||||
this._isMounted = null
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showNsfwHelp: false,
|
showNsfwHelp: false,
|
||||||
isHidden: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,31 +26,11 @@ class FileTile extends React.Component {
|
||||||
uri,
|
uri,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
this._isMounted = true;
|
|
||||||
|
|
||||||
if (this.props.hideOnRemove) {
|
|
||||||
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isResolvingUri && !claim && uri) {
|
if(!isResolvingUri && !claim && uri) {
|
||||||
resolveUri(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() {
|
handleMouseOver() {
|
||||||
if (this.props.obscureNsfw && this.props.metadata && this.props.metadata.nsfw) {
|
if (this.props.obscureNsfw && this.props.metadata && this.props.metadata.nsfw) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -71,10 +48,6 @@ class FileTile extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.isHidden) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
claim,
|
claim,
|
||||||
metadata,
|
metadata,
|
||||||
|
@ -86,18 +59,22 @@ class FileTile extends React.Component {
|
||||||
|
|
||||||
const uri = lbryuri.normalize(this.props.uri);
|
const uri = lbryuri.normalize(this.props.uri);
|
||||||
const isClaimed = !!claim;
|
const isClaimed = !!claim;
|
||||||
|
const isClaimable = lbryuri.isClaimable(uri)
|
||||||
const title = isClaimed && metadata && metadata.title ? metadata.title : uri;
|
const title = isClaimed && metadata && metadata.title ? metadata.title : uri;
|
||||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||||
let onClick = () => navigate('/show', { uri })
|
let onClick = () => navigate('/show', { uri })
|
||||||
|
|
||||||
let description = ""
|
let description = ""
|
||||||
if (isClaimed) {
|
if (isClaimed) {
|
||||||
description = metadata.description
|
description = metadata && metadata.description
|
||||||
} else if (isResolvingUri) {
|
} else if (isResolvingUri) {
|
||||||
description = "Loading..."
|
description = "Loading..."
|
||||||
} else if (showEmpty === FileTile.SHOW_EMPTY_PUBLISH) {
|
} else if (showEmpty === FileTile.SHOW_EMPTY_PUBLISH) {
|
||||||
onClick = () => navigate('/publish')
|
onClick = () => navigate('/publish', { })
|
||||||
description = <span className="empty">This location is unclaimed - <span className="button-text">put something here</span>!</span>
|
description = <span className="empty">
|
||||||
|
This location is unused. { ' ' }
|
||||||
|
{ isClaimable && <span className="button-text">Put something here!</span> }
|
||||||
|
</span>
|
||||||
} else if (showEmpty === FileTile.SHOW_EMPTY_PENDING) {
|
} else if (showEmpty === FileTile.SHOW_EMPTY_PENDING) {
|
||||||
description = <span className="empty">This file is pending confirmation.</span>
|
description = <span className="empty">This file is pending confirmation.</span>
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,13 @@ import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Router from './view.jsx';
|
import Router from './view.jsx';
|
||||||
import {
|
import {
|
||||||
selectCurrentPage
|
selectCurrentPage,
|
||||||
|
selectCurrentParams,
|
||||||
} from 'selectors/app.js';
|
} from 'selectors/app.js';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
|
params: selectCurrentParams(state),
|
||||||
currentPage: selectCurrentPage(state)
|
currentPage: selectCurrentPage(state)
|
||||||
})
|
})
|
||||||
|
|
||||||
const perform = {
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(select, null)(Router);
|
export default connect(select, null)(Router);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SettingsPage from 'page/settings.js';
|
import SettingsPage from 'page/settings';
|
||||||
import HelpPage from 'page/help';
|
import HelpPage from 'page/help';
|
||||||
import ReportPage from 'page/report.js';
|
import ReportPage from 'page/report.js';
|
||||||
import StartPage from 'page/start.js';
|
import StartPage from 'page/start.js';
|
||||||
|
@ -25,25 +25,26 @@ const route = (page, routesMap) => {
|
||||||
const Router = (props) => {
|
const Router = (props) => {
|
||||||
const {
|
const {
|
||||||
currentPage,
|
currentPage,
|
||||||
|
params,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return route(currentPage, {
|
return route(currentPage, {
|
||||||
'settings': <SettingsPage {...props} />,
|
'settings': <SettingsPage {...params} />,
|
||||||
'help': <HelpPage {...props} />,
|
'help': <HelpPage {...params} />,
|
||||||
'report': <ReportPage {...props} />,
|
'report': <ReportPage {...params} />,
|
||||||
'downloaded': <FileListDownloaded {...props} />,
|
'downloaded': <FileListDownloaded {...params} />,
|
||||||
'published': <FileListPublished {...props} />,
|
'published': <FileListPublished {...params} />,
|
||||||
'start': <StartPage {...props} />,
|
'start': <StartPage {...params} />,
|
||||||
'wallet': <WalletPage {...props} />,
|
'wallet': <WalletPage {...params} />,
|
||||||
'send': <WalletPage {...props} />,
|
'send': <WalletPage {...params} />,
|
||||||
'receive': <WalletPage {...props} />,
|
'receive': <WalletPage {...params} />,
|
||||||
'show': <ShowPage {...props} />,
|
'show': <ShowPage {...params} />,
|
||||||
'channel': <ChannelPage {...props} />,
|
'channel': <ChannelPage {...params} />,
|
||||||
'publish': <PublishPage {...props} />,
|
'publish': <PublishPage {...params} />,
|
||||||
'developer': <DeveloperPage {...props} />,
|
'developer': <DeveloperPage {...params} />,
|
||||||
'discover': <DiscoverPage {...props} />,
|
'discover': <DiscoverPage {...params} />,
|
||||||
'rewards': <RewardsPage {...props} />,
|
'rewards': <RewardsPage {...params} />,
|
||||||
'search': <SearchPage {...props} />,
|
'search': <SearchPage {...params} />,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,34 +9,46 @@ import {
|
||||||
selectCurrentModal,
|
selectCurrentModal,
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
import {
|
import {
|
||||||
doWatchVideo,
|
doPurchaseUri,
|
||||||
doLoadVideo,
|
doLoadVideo,
|
||||||
} from 'actions/content'
|
} from 'actions/content'
|
||||||
import {
|
import {
|
||||||
selectLoadingCurrentUri,
|
makeSelectMetadataForUri
|
||||||
selectCurrentUriFileReadyToPlay,
|
} from 'selectors/claims'
|
||||||
selectCurrentUriIsPlaying,
|
import {
|
||||||
selectCurrentUriFileInfo,
|
makeSelectFileInfoForUri,
|
||||||
selectDownloadingCurrentUri,
|
makeSelectLoadingForUri,
|
||||||
|
makeSelectDownloadingForUri,
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriCostInfo,
|
makeSelectCostInfoForUri,
|
||||||
} from 'selectors/cost_info'
|
} from 'selectors/cost_info'
|
||||||
import Video from './view'
|
import Video from './view'
|
||||||
|
|
||||||
const select = (state) => ({
|
|
||||||
costInfo: selectCurrentUriCostInfo(state),
|
const makeSelect = () => {
|
||||||
fileInfo: selectCurrentUriFileInfo(state),
|
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),
|
modal: selectCurrentModal(state),
|
||||||
isLoading: selectLoadingCurrentUri(state),
|
isLoading: selectIsLoading(state, props),
|
||||||
readyToPlay: selectCurrentUriFileReadyToPlay(state),
|
isDownloading: selectIsDownloading(state, props),
|
||||||
isDownloading: selectDownloadingCurrentUri(state),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return select
|
||||||
|
}
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
loadVideo: () => dispatch(doLoadVideo()),
|
loadVideo: (uri) => dispatch(doLoadVideo(uri)),
|
||||||
watchVideo: (elem) => dispatch(doWatchVideo()),
|
purchaseUri: (uri) => dispatch(doPurchaseUri(uri)),
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(select, perform)(Video)
|
export default connect(makeSelect, perform)(Video)
|
|
@ -1,17 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
|
||||||
Icon,
|
|
||||||
Thumbnail,
|
|
||||||
} from 'component/common';
|
|
||||||
import FilePrice from 'component/filePrice'
|
import FilePrice from 'component/filePrice'
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import Modal from 'component/modal';
|
import Modal from 'component/modal';
|
||||||
|
|
||||||
class WatchLink extends React.Component {
|
class VideoPlayButton extends React.Component {
|
||||||
confirmPurchaseClick() {
|
onPurchaseConfirmed() {
|
||||||
this.props.closeModal()
|
this.props.closeModal()
|
||||||
this.props.startPlaying()
|
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() {
|
render() {
|
||||||
|
@ -19,7 +23,6 @@ class WatchLink extends React.Component {
|
||||||
button,
|
button,
|
||||||
label,
|
label,
|
||||||
className,
|
className,
|
||||||
onWatchClick,
|
|
||||||
metadata,
|
metadata,
|
||||||
metadata: {
|
metadata: {
|
||||||
title,
|
title,
|
||||||
|
@ -32,13 +35,21 @@ class WatchLink extends React.Component {
|
||||||
fileInfo,
|
fileInfo,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
|
/*
|
||||||
|
title={
|
||||||
|
isLoading ? "Video is Loading" :
|
||||||
|
!costInfo ? "Waiting on cost info..." :
|
||||||
|
fileInfo === undefined ? "Waiting on file info..." : ""
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return (<div>
|
return (<div>
|
||||||
<Link button={ button ? button : null }
|
<Link button={ button ? button : null }
|
||||||
disabled={isLoading || costInfo.cost == undefined || fileInfo === undefined}
|
disabled={isLoading || fileInfo === undefined || (fileInfo === null && (!costInfo || costInfo.cost === undefined))}
|
||||||
label={label ? label : ""}
|
label={label ? label : ""}
|
||||||
className="video__play-button"
|
className="video__play-button"
|
||||||
icon="icon-play"
|
icon="icon-play"
|
||||||
onClick={onWatchClick} />
|
onClick={this.onWatchClick.bind(this)} />
|
||||||
{modal}
|
{modal}
|
||||||
<Modal contentLabel="Not enough credits" isOpen={modal == 'notEnoughCredits'} onConfirmed={closeModal}>
|
<Modal contentLabel="Not enough credits" isOpen={modal == 'notEnoughCredits'} onConfirmed={closeModal}>
|
||||||
You don't have enough LBRY credits to pay for this stream.
|
You don't have enough LBRY credits to pay for this stream.
|
||||||
|
@ -47,9 +58,9 @@ class WatchLink extends React.Component {
|
||||||
type="confirm"
|
type="confirm"
|
||||||
isOpen={modal == 'affirmPurchase'}
|
isOpen={modal == 'affirmPurchase'}
|
||||||
contentLabel="Confirm Purchase"
|
contentLabel="Confirm Purchase"
|
||||||
onConfirmed={this.confirmPurchaseClick.bind(this)}
|
onConfirmed={this.onPurchaseConfirmed.bind(this)}
|
||||||
onAborted={closeModal}>
|
onAborted={closeModal}>
|
||||||
Are you sure you'd like to buy <strong>{this.props.metadata.title}</strong> for <strong><FilePrice uri={uri} look="plain" /></strong> credits?
|
This will purchase <strong>{title}</strong> for <strong><FilePrice uri={uri} look="plain" /></strong> credits.
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={modal == 'timedOut'} onConfirmed={closeModal} contentLabel="Timed Out">
|
isOpen={modal == 'timedOut'} onConfirmed={closeModal} contentLabel="Timed Out">
|
||||||
|
@ -67,16 +78,6 @@ class Video extends React.Component {
|
||||||
this.state = { isPlaying: false }
|
this.state = { isPlaying: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
onWatchClick() {
|
|
||||||
this.props.watchVideo().then(() => {
|
|
||||||
if (!this.props.modal) {
|
|
||||||
this.setState({
|
|
||||||
isPlaying: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
startPlaying() {
|
startPlaying() {
|
||||||
this.setState({
|
this.setState({
|
||||||
isPlaying: true
|
isPlaying: true
|
||||||
|
@ -85,8 +86,6 @@ class Video extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
readyToPlay = false,
|
|
||||||
thumbnail,
|
|
||||||
metadata,
|
metadata,
|
||||||
isLoading,
|
isLoading,
|
||||||
isDownloading,
|
isDownloading,
|
||||||
|
@ -96,6 +95,8 @@ class Video extends React.Component {
|
||||||
isPlaying = false,
|
isPlaying = false,
|
||||||
} = this.state
|
} = this.state
|
||||||
|
|
||||||
|
const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0
|
||||||
|
|
||||||
let loadStatusMessage = ''
|
let loadStatusMessage = ''
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
@ -105,39 +106,42 @@ class Video extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"video " + this.props.className + (isPlaying && readyToPlay ? " video--active" : " video--hidden")}>{
|
<div className={"video " + this.props.className + (isPlaying ? " video--active" : " video--hidden")}>{
|
||||||
isPlaying ?
|
isPlaying || isLoading ?
|
||||||
!readyToPlay ?
|
(!isReadyToPlay ?
|
||||||
<span>this is the world's worst loading screen and we shipped our software with it anyway... <br /><br />{loadStatusMessage}</span> :
|
<span>this is the world's worst loading screen and we shipped our software with it anyway... <br /><br />{loadStatusMessage}</span> :
|
||||||
<VideoPlayer downloadPath={fileInfo.download_path} /> :
|
<VideoPlayer poster={metadata.thumbnail} autoplay={isPlaying} downloadPath={fileInfo.download_path} />) :
|
||||||
<div className="video__cover" style={{backgroundImage: 'url("' + metadata.thumbnail + '")'}}>
|
<div className="video__cover" style={{backgroundImage: 'url("' + metadata.thumbnail + '")'}}>
|
||||||
<WatchLink icon="icon-play" onWatchClick={this.onWatchClick.bind(this)}
|
<VideoPlayButton startPlaying={this.startPlaying.bind(this)} {...this.props} />
|
||||||
startPlaying={this.startPlaying.bind(this)} {...this.props}></WatchLink>
|
|
||||||
</div>
|
</div>
|
||||||
}</div>
|
}</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VideoPlayer extends React.PureComponent {
|
class VideoPlayer extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const elem = this.refs.video
|
const elem = this.refs.video
|
||||||
const {
|
const {
|
||||||
|
autoplay,
|
||||||
downloadPath,
|
downloadPath,
|
||||||
contentType,
|
contentType,
|
||||||
} = this.props
|
} = this.props
|
||||||
const players = plyr.setup(elem)
|
const players = plyr.setup(elem)
|
||||||
|
if (autoplay) {
|
||||||
players[0].play()
|
players[0].play()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
downloadPath,
|
downloadPath,
|
||||||
contentType,
|
contentType,
|
||||||
|
poster,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<video controls id="video" ref="video">
|
<video controls id="video" ref="video" style={{backgroundImage: "url('" + poster + "')"}} >
|
||||||
<source src={downloadPath} type={contentType} />
|
<source src={downloadPath} type={contentType} />
|
||||||
</video>
|
</video>
|
||||||
)
|
)
|
||||||
|
|
|
@ -99,12 +99,20 @@ class WunderBar extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
this._input.value = this.state.address;
|
if (this._input) {
|
||||||
if (this._input && this._focusPending) {
|
const start = this._input.selectionStart,
|
||||||
|
end = this._input.selectionEnd;
|
||||||
|
|
||||||
|
this._input.value = this.state.address; //this causes cursor to go to end of input
|
||||||
|
|
||||||
|
this._input.setSelectionRange(start, end);
|
||||||
|
|
||||||
|
if (this._focusPending) {
|
||||||
this._input.select();
|
this._input.select();
|
||||||
this._focusPending = false;
|
this._focusPending = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onKeyPress(event) {
|
onKeyPress(event) {
|
||||||
if (event.charCode == 13 && this._input.value) {
|
if (event.charCode == 13 && this._input.value) {
|
||||||
|
|
|
@ -35,10 +35,13 @@ export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED'
|
||||||
export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED'
|
export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED'
|
||||||
export const RESOLVE_URI_STARTED = 'RESOLVE_URI_STARTED'
|
export const RESOLVE_URI_STARTED = 'RESOLVE_URI_STARTED'
|
||||||
export const RESOLVE_URI_COMPLETED = 'RESOLVE_URI_COMPLETED'
|
export const RESOLVE_URI_COMPLETED = 'RESOLVE_URI_COMPLETED'
|
||||||
export const FETCH_DOWNLOADED_CONTENT_STARTED = 'FETCH_DOWNLOADED_CONTENT_STARTED'
|
export const RESOLVE_URI_CANCELED = 'RESOLVE_URI_CANCELED'
|
||||||
export const FETCH_DOWNLOADED_CONTENT_COMPLETED = 'FETCH_DOWNLOADED_CONTENT_COMPLETED'
|
export const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED'
|
||||||
export const FETCH_PUBLISHED_CONTENT_STARTED = 'FETCH_PUBLISHED_CONTENT_STARTED'
|
export const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_COMPLETED'
|
||||||
export const FETCH_PUBLISHED_CONTENT_COMPLETED = 'FETCH_PUBLISHED_CONTENT_COMPLETED'
|
export const CLAIM_LIST_MINE_STARTED = 'CLAIM_LIST_MINE_STARTED'
|
||||||
|
export const CLAIM_LIST_MINE_COMPLETED = 'CLAIM_LIST_MINE_COMPLETED'
|
||||||
|
export const FILE_LIST_STARTED = 'FILE_LIST_STARTED'
|
||||||
|
export const FILE_LIST_COMPLETED = 'FILE_LIST_COMPLETED'
|
||||||
export const FETCH_FILE_INFO_STARTED = 'FETCH_FILE_INFO_STARTED'
|
export const FETCH_FILE_INFO_STARTED = 'FETCH_FILE_INFO_STARTED'
|
||||||
export const FETCH_FILE_INFO_COMPLETED = 'FETCH_FILE_INFO_COMPLETED'
|
export const FETCH_FILE_INFO_COMPLETED = 'FETCH_FILE_INFO_COMPLETED'
|
||||||
export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED'
|
export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED'
|
||||||
|
@ -52,11 +55,12 @@ export const DOWNLOADING_COMPLETED = 'DOWNLOADING_COMPLETED'
|
||||||
export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED'
|
export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED'
|
||||||
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED'
|
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED'
|
||||||
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED'
|
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED'
|
||||||
export const DELETE_FILE_STARTED = 'DELETE_FILE_STARTED'
|
export const FILE_DELETE = 'FILE_DELETE'
|
||||||
export const DELETE_FILE_COMPLETED = 'DELETE_FILE_COMPLETED'
|
|
||||||
export const FETCH_MY_CLAIMS_COMPLETED = 'FETCH_MY_CLAIMS_COMPLETED'
|
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
export const SEARCH_STARTED = 'SEARCH_STARTED'
|
export const SEARCH_STARTED = 'SEARCH_STARTED'
|
||||||
export const SEARCH_COMPLETED = 'SEARCH_COMPLETED'
|
export const SEARCH_COMPLETED = 'SEARCH_COMPLETED'
|
||||||
export const SEARCH_CANCELLED = 'SEARCH_CANCELLED'
|
export const SEARCH_CANCELLED = 'SEARCH_CANCELLED'
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED'
|
|
@ -68,6 +68,8 @@ jsonrpc.call = function (connectionString, method, params, callback, errorCallba
|
||||||
}));
|
}));
|
||||||
|
|
||||||
sessionStorage.setItem('JSONRPCCounter', counter + 1);
|
sessionStorage.setItem('JSONRPCCounter', counter + 1);
|
||||||
|
|
||||||
|
return xhr
|
||||||
};
|
};
|
||||||
|
|
||||||
export default jsonrpc;
|
export default jsonrpc;
|
||||||
|
|
281
ui/js/lbry.js
281
ui/js/lbry.js
|
@ -7,6 +7,25 @@ import {getLocal, getSession, setSession, setLocal} from './utils.js';
|
||||||
const {remote, ipcRenderer} = require('electron');
|
const {remote, ipcRenderer} = require('electron');
|
||||||
const menu = remote.require('./menu/main-menu');
|
const menu = remote.require('./menu/main-menu');
|
||||||
|
|
||||||
|
let lbry = {
|
||||||
|
isConnected: false,
|
||||||
|
daemonConnectionString: 'http://localhost:5279/lbryapi',
|
||||||
|
webUiUri: 'http://localhost:5279',
|
||||||
|
peerListTimeout: 6000,
|
||||||
|
pendingPublishTimeout: 20 * 60 * 1000,
|
||||||
|
colors: {
|
||||||
|
primary: '#155B4A'
|
||||||
|
},
|
||||||
|
defaultClientSettings: {
|
||||||
|
showNsfw: false,
|
||||||
|
showUnavailable: true,
|
||||||
|
debug: false,
|
||||||
|
useCustomLighthouseServers: false,
|
||||||
|
customLighthouseServers: [],
|
||||||
|
showDeveloperMenu: false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records a publish attempt in local storage. Returns a dictionary with all the data needed to
|
* Records a publish attempt in local storage. Returns a dictionary with all the data needed to
|
||||||
* needed to make a dummy claim or file info object.
|
* needed to make a dummy claim or file info object.
|
||||||
|
@ -40,14 +59,14 @@ function removePendingPublishIfNeeded({name, channel_name, outpoint}) {
|
||||||
return pub.outpoint === outpoint || (pub.name === name && (!channel_name || pub.channel_name === channel_name));
|
return pub.outpoint === outpoint || (pub.name === name && (!channel_name || pub.channel_name === channel_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
setLocal('pendingPublishes', getPendingPublishes().filter(pub => !pubMatches(pub)));
|
setLocal('pendingPublishes', lbry.getPendingPublishes().filter(pub => !pubMatches(pub)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current list of pending publish attempts. Filters out any that have timed out and
|
* Gets the current list of pending publish attempts. Filters out any that have timed out and
|
||||||
* removes them from the list.
|
* removes them from the list.
|
||||||
*/
|
*/
|
||||||
function getPendingPublishes() {
|
lbry.getPendingPublishes = function() {
|
||||||
const pendingPublishes = getLocal('pendingPublishes') || [];
|
const pendingPublishes = getLocal('pendingPublishes') || [];
|
||||||
const newPendingPublishes = pendingPublishes.filter(pub => Date.now() - pub.time <= lbry.pendingPublishTimeout);
|
const newPendingPublishes = pendingPublishes.filter(pub => Date.now() - pub.time <= lbry.pendingPublishTimeout);
|
||||||
setLocal('pendingPublishes', newPendingPublishes);
|
setLocal('pendingPublishes', newPendingPublishes);
|
||||||
|
@ -59,7 +78,7 @@ function getPendingPublishes() {
|
||||||
* provided along withe the name. If no pending publish is found, returns null.
|
* provided along withe the name. If no pending publish is found, returns null.
|
||||||
*/
|
*/
|
||||||
function getPendingPublish({name, channel_name, outpoint}) {
|
function getPendingPublish({name, channel_name, outpoint}) {
|
||||||
const pendingPublishes = getPendingPublishes();
|
const pendingPublishes = lbry.getPendingPublishes();
|
||||||
return pendingPublishes.find(
|
return pendingPublishes.find(
|
||||||
pub => pub.outpoint === outpoint || (pub.name === name && (!channel_name || pub.channel_name === channel_name))
|
pub => pub.outpoint === outpoint || (pub.name === name && (!channel_name || pub.channel_name === channel_name))
|
||||||
) || null;
|
) || null;
|
||||||
|
@ -74,28 +93,8 @@ function pendingPublishToDummyFileInfo({name, outpoint, claim_id}) {
|
||||||
}
|
}
|
||||||
window.pptdfi = pendingPublishToDummyFileInfo;
|
window.pptdfi = pendingPublishToDummyFileInfo;
|
||||||
|
|
||||||
let lbry = {
|
|
||||||
isConnected: false,
|
|
||||||
rootPath: '.',
|
|
||||||
daemonConnectionString: 'http://localhost:5279/lbryapi',
|
|
||||||
webUiUri: 'http://localhost:5279',
|
|
||||||
peerListTimeout: 6000,
|
|
||||||
pendingPublishTimeout: 20 * 60 * 1000,
|
|
||||||
colors: {
|
|
||||||
primary: '#155B4A'
|
|
||||||
},
|
|
||||||
defaultClientSettings: {
|
|
||||||
showNsfw: false,
|
|
||||||
showUnavailable: true,
|
|
||||||
debug: false,
|
|
||||||
useCustomLighthouseServers: false,
|
|
||||||
customLighthouseServers: [],
|
|
||||||
showDeveloperMenu: false,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
lbry.call = function (method, params, callback, errorCallback, connectFailedCallback) {
|
lbry.call = function (method, params, callback, errorCallback, connectFailedCallback) {
|
||||||
jsonrpc.call(lbry.daemonConnectionString, method, params, callback, errorCallback, connectFailedCallback);
|
return jsonrpc.call(lbry.daemonConnectionString, method, params, callback, errorCallback, connectFailedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
//core
|
//core
|
||||||
|
@ -151,69 +150,14 @@ lbry.isDaemonAcceptingConnections = function (callback) {
|
||||||
lbry.call('status', {}, () => callback(true), null, () => callback(false))
|
lbry.call('status', {}, () => callback(true), null, () => callback(false))
|
||||||
};
|
};
|
||||||
|
|
||||||
lbry.checkFirstRun = function(callback) {
|
|
||||||
lbry.call('is_first_run', {}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getUnusedAddress = function(callback) {
|
|
||||||
lbry.call('wallet_unused_address', {}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.checkAddressIsMine = function(address, callback) {
|
lbry.checkAddressIsMine = function(address, callback) {
|
||||||
lbry.call('address_is_mine', {address: address}, callback);
|
lbry.call('address_is_mine', {address: address}, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.getDaemonSettings = function(callback) {
|
|
||||||
lbry.call('get_settings', {}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.setDaemonSettings = function(settings, callback) {
|
|
||||||
lbry.call('set_settings', settings, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.setDaemonSetting = function(setting, value, callback) {
|
|
||||||
var setSettingsArgs = {};
|
|
||||||
setSettingsArgs[setting] = value;
|
|
||||||
lbry.call('set_settings', setSettingsArgs, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
lbry.getBalance = function(callback) {
|
|
||||||
lbry.call("wallet_balance", {}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.sendToAddress = function(amount, address, callback, errorCallback) {
|
lbry.sendToAddress = function(amount, address, callback, errorCallback) {
|
||||||
lbry.call("send_amount_to_address", { "amount" : amount, "address": address }, callback, errorCallback);
|
lbry.call("send_amount_to_address", { "amount" : amount, "address": address }, callback, errorCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.getClaimInfo = function(name, callback) {
|
|
||||||
if (!name) {
|
|
||||||
throw new Error(`Name required.`);
|
|
||||||
}
|
|
||||||
lbry.call('get_claim_info', { name: name }, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getMyClaim = function(name, callback) {
|
|
||||||
lbry.call('claim_list_mine', {}, (claims) => {
|
|
||||||
callback(claims.find((claim) => claim.name == name) || null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getPeersForBlobHash = function(blobHash, callback) {
|
|
||||||
let timedOut = false;
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
timedOut = true;
|
|
||||||
callback([]);
|
|
||||||
}, lbry.peerListTimeout);
|
|
||||||
|
|
||||||
lbry.call('peer_list', { blob_hash: blobHash }, function(peers) {
|
|
||||||
if (!timedOut) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
callback(peers);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a LBRY URI; will first try and calculate a total cost using
|
* Takes a LBRY URI; will first try and calculate a total cost using
|
||||||
* Lighthouse. If Lighthouse can't be reached, it just retrives the
|
* Lighthouse. If Lighthouse can't be reached, it just retrives the
|
||||||
|
@ -251,33 +195,9 @@ lbry.getCostInfo = function(uri) {
|
||||||
}, reject);
|
}, reject);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCostGenerous(uri) {
|
|
||||||
// If generous is on, the calculation is simple enough that we might as well do it here in the front end
|
|
||||||
lbry.resolve({uri: uri}).then((resolutionInfo) => {
|
|
||||||
if (!resolutionInfo) {
|
|
||||||
return reject(new Error("Unused URI"));
|
|
||||||
}
|
|
||||||
const fee = resolutionInfo.claim.value.stream.metadata.fee;
|
|
||||||
if (fee === undefined) {
|
|
||||||
cacheAndResolve(0, true);
|
|
||||||
} else if (fee.currency == 'LBC') {
|
|
||||||
cacheAndResolve(fee.amount, true);
|
|
||||||
} else {
|
|
||||||
lbryio.getExchangeRates().then(({lbc_usd}) => {
|
|
||||||
cacheAndResolve(fee.amount / lbc_usd, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const uriObj = lbryuri.parse(uri);
|
const uriObj = lbryuri.parse(uri);
|
||||||
const name = uriObj.path || uriObj.name;
|
const name = uriObj.path || uriObj.name;
|
||||||
|
|
||||||
lbry.settings_get({allow_cached: true}).then(({is_generous_host}) => {
|
|
||||||
if (is_generous_host) {
|
|
||||||
return getCostGenerous(uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
lighthouse.get_size_for_name(name).then((size) => {
|
lighthouse.get_size_for_name(name).then((size) => {
|
||||||
if (size) {
|
if (size) {
|
||||||
getCost(name, size);
|
getCost(name, size);
|
||||||
|
@ -285,49 +205,12 @@ lbry.getCostInfo = function(uri) {
|
||||||
else {
|
else {
|
||||||
getCost(name, null);
|
getCost(name, null);
|
||||||
}
|
}
|
||||||
}, () => {
|
})
|
||||||
getCost(name, null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return lbry.costPromiseCache[uri];
|
return lbry.costPromiseCache[uri];
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.getMyClaims = function(callback) {
|
|
||||||
lbry.call('get_name_claims', {}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.removeFile = function(outpoint, deleteTargetFile=true, callback) {
|
|
||||||
this._removedFiles.push(outpoint);
|
|
||||||
// this._updateFileInfoSubscribers(outpoint);
|
|
||||||
|
|
||||||
lbry.file_delete({
|
|
||||||
outpoint: outpoint,
|
|
||||||
delete_target_file: deleteTargetFile,
|
|
||||||
}).then(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getFileInfoWhenListed = function(name, callback, timeoutCallback, tryNum=0) {
|
|
||||||
function scheduleNextCheckOrTimeout() {
|
|
||||||
if (timeoutCallback && tryNum > 200) {
|
|
||||||
timeoutCallback();
|
|
||||||
} else {
|
|
||||||
setTimeout(() => lbry.getFileInfoWhenListed(name, callback, timeoutCallback, tryNum + 1), 250);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calls callback with file info when it appears in the lbrynet file manager.
|
|
||||||
// If timeoutCallback is provided, it will be called if the file fails to appear.
|
|
||||||
lbry.file_list({name: name}).then(([fileInfo]) => {
|
|
||||||
if (fileInfo) {
|
|
||||||
callback(fileInfo);
|
|
||||||
} else {
|
|
||||||
scheduleNextCheckOrTimeout();
|
|
||||||
}
|
|
||||||
}, () => scheduleNextCheckOrTimeout());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publishes a file. The optional fileListedCallback is called when the file becomes available in
|
* Publishes a file. The optional fileListedCallback is called when the file becomes available in
|
||||||
* lbry.file_list() during the publish process.
|
* lbry.file_list() during the publish process.
|
||||||
|
@ -368,10 +251,6 @@ lbry.publish = function(params, fileListedCallback, publishedCallback, errorCall
|
||||||
fileListedCallback(true);
|
fileListedCallback(true);
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
//lbry.getFileInfoWhenListed(params.name, function(fileInfo) {
|
|
||||||
// fileListedCallback(fileInfo);
|
|
||||||
//});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -426,29 +305,10 @@ lbry.formatName = function(name) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.nameIsValid = function(name, checkCase=true) {
|
|
||||||
const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
|
|
||||||
return regexp.test(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.loadJs = function(src, type, onload)
|
|
||||||
{
|
|
||||||
var lbryScriptTag = document.getElementById('lbry'),
|
|
||||||
newScriptTag = document.createElement('script'),
|
|
||||||
type = type || 'text/javascript';
|
|
||||||
|
|
||||||
newScriptTag.src = src;
|
|
||||||
newScriptTag.type = type;
|
|
||||||
if (onload)
|
|
||||||
{
|
|
||||||
newScriptTag.onload = onload;
|
|
||||||
}
|
|
||||||
lbryScriptTag.parentNode.insertBefore(newScriptTag, lbryScriptTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.imagePath = function(file)
|
lbry.imagePath = function(file)
|
||||||
{
|
{
|
||||||
return lbry.rootPath + '/img/' + file;
|
return 'img/' + file;
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.getMediaType = function(contentType, fileName) {
|
lbry.getMediaType = function(contentType, fileName) {
|
||||||
|
@ -479,71 +339,9 @@ lbry.stop = function(callback) {
|
||||||
lbry.call('stop', {}, callback);
|
lbry.call('stop', {}, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
lbry.fileInfo = {};
|
|
||||||
lbry._subscribeIdCount = 0;
|
lbry._subscribeIdCount = 0;
|
||||||
lbry._fileInfoSubscribeCallbacks = {};
|
|
||||||
lbry._fileInfoSubscribeInterval = 500000;
|
|
||||||
lbry._balanceSubscribeCallbacks = {};
|
lbry._balanceSubscribeCallbacks = {};
|
||||||
lbry._balanceSubscribeInterval = 5000;
|
lbry._balanceSubscribeInterval = 5000;
|
||||||
lbry._removedFiles = [];
|
|
||||||
lbry._claimIdOwnershipCache = {};
|
|
||||||
|
|
||||||
lbry._updateClaimOwnershipCache = function(claimId) {
|
|
||||||
lbry.getMyClaims((claimInfos) => {
|
|
||||||
lbry._claimIdOwnershipCache[claimId] = !!claimInfos.reduce(function(match, claimInfo) {
|
|
||||||
return match || claimInfo.claim_id == claimId;
|
|
||||||
}, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
lbry._updateFileInfoSubscribers = function(outpoint) {
|
|
||||||
const callSubscribedCallbacks = (outpoint, fileInfo) => {
|
|
||||||
for (let callback of Object.values(this._fileInfoSubscribeCallbacks[outpoint])) {
|
|
||||||
callback(fileInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lbry._removedFiles.includes(outpoint)) {
|
|
||||||
callSubscribedCallbacks(outpoint, false);
|
|
||||||
} else {
|
|
||||||
lbry.file_list({
|
|
||||||
outpoint: outpoint,
|
|
||||||
full_status: true,
|
|
||||||
}).then(([fileInfo]) => {
|
|
||||||
if (fileInfo) {
|
|
||||||
if (this._claimIdOwnershipCache[fileInfo.claim_id] === undefined) {
|
|
||||||
this._updateClaimOwnershipCache(fileInfo.claim_id);
|
|
||||||
}
|
|
||||||
fileInfo.isMine = !!this._claimIdOwnershipCache[fileInfo.claim_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
callSubscribedCallbacks(outpoint, fileInfo);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(this._fileInfoSubscribeCallbacks[outpoint]).length) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this._updateFileInfoSubscribers(outpoint);
|
|
||||||
}, lbry._fileInfoSubscribeInterval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.fileInfoSubscribe = function(outpoint, callback) {
|
|
||||||
if (!lbry._fileInfoSubscribeCallbacks[outpoint])
|
|
||||||
{
|
|
||||||
lbry._fileInfoSubscribeCallbacks[outpoint] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscribeId = ++lbry._subscribeIdCount;
|
|
||||||
lbry._fileInfoSubscribeCallbacks[outpoint][subscribeId] = callback;
|
|
||||||
lbry._updateFileInfoSubscribers(outpoint);
|
|
||||||
return subscribeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.fileInfoUnsubscribe = function(outpoint, subscribeId) {
|
|
||||||
delete lbry._fileInfoSubscribeCallbacks[outpoint][subscribeId];
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry._balanceUpdateInterval = null;
|
lbry._balanceUpdateInterval = null;
|
||||||
lbry._updateBalanceSubscribers = function() {
|
lbry._updateBalanceSubscribers = function() {
|
||||||
|
@ -620,7 +418,7 @@ lbry.file_list = function(params={}) {
|
||||||
lbry.call('file_list', params, (fileInfos) => {
|
lbry.call('file_list', params, (fileInfos) => {
|
||||||
removePendingPublishIfNeeded({name, channel_name, outpoint});
|
removePendingPublishIfNeeded({name, channel_name, outpoint});
|
||||||
|
|
||||||
const dummyFileInfos = getPendingPublishes().map(pendingPublishToDummyFileInfo);
|
const dummyFileInfos = lbry.getPendingPublishes().map(pendingPublishToDummyFileInfo);
|
||||||
resolve([...fileInfos, ...dummyFileInfos]);
|
resolve([...fileInfos, ...dummyFileInfos]);
|
||||||
}, reject, reject);
|
}, reject, reject);
|
||||||
});
|
});
|
||||||
|
@ -633,14 +431,15 @@ lbry.claim_list_mine = function(params={}) {
|
||||||
removePendingPublishIfNeeded({name, channel_name, outpoint: txid + ':' + nout});
|
removePendingPublishIfNeeded({name, channel_name, outpoint: txid + ':' + nout});
|
||||||
}
|
}
|
||||||
|
|
||||||
const dummyClaims = getPendingPublishes().map(pendingPublishToDummyClaim);
|
const dummyClaims = lbry.getPendingPublishes().map(pendingPublishToDummyClaim);
|
||||||
resolve([...claims, ...dummyClaims]);
|
resolve([...claims, ...dummyClaims]);
|
||||||
}, reject, reject)
|
}, reject, reject)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const claimCacheKey = 'resolve_claim_cache';
|
const claimCacheKey = 'resolve_claim_cache';
|
||||||
lbry._claimCache = getLocal(claimCacheKey, {});
|
lbry._claimCache = getSession(claimCacheKey, {});
|
||||||
|
lbry._resolveXhrs = {}
|
||||||
lbry.resolve = function(params={}) {
|
lbry.resolve = function(params={}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!params.uri) {
|
if (!params.uri) {
|
||||||
|
@ -649,28 +448,22 @@ lbry.resolve = function(params={}) {
|
||||||
if (params.uri && lbry._claimCache[params.uri] !== undefined) {
|
if (params.uri && lbry._claimCache[params.uri] !== undefined) {
|
||||||
resolve(lbry._claimCache[params.uri]);
|
resolve(lbry._claimCache[params.uri]);
|
||||||
} else {
|
} else {
|
||||||
lbry.call('resolve', params, function(data) {
|
lbry._resolveXhrs[params.uri] = lbry.call('resolve', params, function(data) {
|
||||||
|
if (data !== undefined) {
|
||||||
lbry._claimCache[params.uri] = data;
|
lbry._claimCache[params.uri] = data;
|
||||||
setLocal(claimCacheKey, lbry._claimCache)
|
}
|
||||||
|
setSession(claimCacheKey, lbry._claimCache)
|
||||||
resolve(data)
|
resolve(data)
|
||||||
}, reject)
|
}, reject)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds caching.
|
lbry.cancelResolve = function(params={}) {
|
||||||
lbry._settingsPromise = null;
|
const xhr = lbry._resolveXhrs[params.uri]
|
||||||
lbry.settings_get = function(params={}) {
|
if (xhr && xhr.readyState > 0 && xhr.readyState < 4) {
|
||||||
if (params.allow_cached && lbry._settingsPromise) {
|
xhr.abort()
|
||||||
return lbry._settingsPromise;
|
|
||||||
}
|
}
|
||||||
lbry._settingsPromise = new Promise((resolve, reject) => {
|
|
||||||
lbry.call('settings_get', {}, (settings) => {
|
|
||||||
setSession('settings', settings);
|
|
||||||
resolve(settings);
|
|
||||||
}, reject);
|
|
||||||
});
|
|
||||||
return lbry._settingsPromise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lbry.get = function(params={}) {
|
// lbry.get = function(params={}) {
|
||||||
|
|
|
@ -13,22 +13,19 @@ const lbryio = {
|
||||||
const CONNECTION_STRING = 'https://api.lbry.io/';
|
const CONNECTION_STRING = 'https://api.lbry.io/';
|
||||||
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
||||||
|
|
||||||
|
lbryio._exchangePromise = null;
|
||||||
|
lbryio._exchangeLastFetched = null;
|
||||||
lbryio.getExchangeRates = function() {
|
lbryio.getExchangeRates = function() {
|
||||||
return new Promise((resolve, reject) => {
|
if (!lbryio._exchangeLastFetched || Date.now() - lbryio._exchangeLastFetched > EXCHANGE_RATE_TIMEOUT) {
|
||||||
const cached = getSession('exchangeRateCache');
|
lbryio._exchangePromise = new Promise((resolve, reject) => {
|
||||||
if (!cached || Date.now() - cached.time > EXCHANGE_RATE_TIMEOUT) {
|
|
||||||
lbryio.call('lbc', 'exchange_rate', {}, 'get', true).then(({lbc_usd, lbc_btc, btc_usd}) => {
|
lbryio.call('lbc', 'exchange_rate', {}, 'get', true).then(({lbc_usd, lbc_btc, btc_usd}) => {
|
||||||
const rates = {lbc_usd, lbc_btc, btc_usd};
|
const rates = {lbc_usd, lbc_btc, btc_usd};
|
||||||
setSession('exchangeRateCache', {
|
|
||||||
rates: rates,
|
|
||||||
time: Date.now(),
|
|
||||||
});
|
|
||||||
resolve(rates);
|
resolve(rates);
|
||||||
|
}).catch(reject);
|
||||||
});
|
});
|
||||||
} else {
|
lbryio._exchangeLastFetched = Date.now();
|
||||||
resolve(cached.rates);
|
|
||||||
}
|
}
|
||||||
});
|
return lbryio._exchangePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
lbryio.call = function(resource, action, params={}, method='get', evenIfDisabled=false) { // evenIfDisabled is just for development, when we may have some calls working and some not
|
lbryio.call = function(resource, action, params={}, method='get', evenIfDisabled=false) { // evenIfDisabled is just for development, when we may have some calls working and some not
|
||||||
|
|
|
@ -165,5 +165,30 @@ lbryuri.normalize= function(uri) {
|
||||||
return lbryuri.build({name, path, claimSequence, bidPosition, claimId});
|
return lbryuri.build({name, path, claimSequence, bidPosition, claimId});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lbryuri.isValid = function(uri) {
|
||||||
|
let parts
|
||||||
|
try {
|
||||||
|
parts = lbryuri.parse(lbryuri.normalize(uri))
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return parts && parts.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbryuri.isValidName = function(name, checkCase=true) {
|
||||||
|
const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
|
||||||
|
return regexp.test(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
lbryuri.isClaimable = function(uri) {
|
||||||
|
let parts
|
||||||
|
try {
|
||||||
|
parts = lbryuri.parse(lbryuri.normalize(uri))
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return parts && parts.name && !parts.claimId && !parts.bidPosition && !parts.claimSequence && !parts.isChannel && !parts.path;
|
||||||
|
}
|
||||||
|
|
||||||
window.lbryuri = lbryuri;
|
window.lbryuri = lbryuri;
|
||||||
export default lbryuri;
|
export default lbryuri;
|
||||||
|
|
|
@ -10,9 +10,16 @@ import {AuthOverlay} from './component/auth.js';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import store from 'store.js';
|
import store from 'store.js';
|
||||||
import {
|
import {
|
||||||
doDaemonReady,
|
|
||||||
doChangePath,
|
doChangePath,
|
||||||
|
doDaemonReady,
|
||||||
|
doHistoryPush
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
doFetchDaemonSettings
|
||||||
|
} from 'actions/settings'
|
||||||
|
import {
|
||||||
|
doFileList
|
||||||
|
} from 'actions/file_info'
|
||||||
import parseQueryParams from 'util/query_params'
|
import parseQueryParams from 'util/query_params'
|
||||||
|
|
||||||
const {remote, ipcRenderer} = require('electron');
|
const {remote, ipcRenderer} = require('electron');
|
||||||
|
@ -27,16 +34,22 @@ window.addEventListener('contextmenu', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('popstate', (event) => {
|
window.addEventListener('popstate', (event, param) => {
|
||||||
const pathname = document.location.pathname
|
|
||||||
const queryString = document.location.search
|
const queryString = document.location.search
|
||||||
if (pathname.match(/dist/)) return
|
const pathParts = document.location.pathname.split('/')
|
||||||
|
const route = '/' + pathParts[pathParts.length - 1]
|
||||||
|
|
||||||
app.store.dispatch(doChangePath(`${pathname}${queryString}`))
|
if (route.match(/html$/)) return
|
||||||
|
|
||||||
|
console.log('title should be set here, but it is not in popstate? TODO')
|
||||||
|
|
||||||
|
app.store.dispatch(doChangePath(`${route}${queryString}`))
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcRenderer.on('open-uri-requested', (event, uri) => {
|
ipcRenderer.on('open-uri-requested', (event, uri) => {
|
||||||
console.log('FIX ME do magic dispatch');
|
if (uri) {
|
||||||
|
console.log('FIX ME do magic dispatch: ' + uri);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const initialState = app.store.getState();
|
const initialState = app.store.getState();
|
||||||
|
@ -46,7 +59,9 @@ var init = function() {
|
||||||
function onDaemonReady() {
|
function onDaemonReady() {
|
||||||
app.store.dispatch(doDaemonReady())
|
app.store.dispatch(doDaemonReady())
|
||||||
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
||||||
window.history.pushState({}, "Discover", '/discover');
|
app.store.dispatch(doHistoryPush({}, "Discover", "/discover"))
|
||||||
|
app.store.dispatch(doFetchDaemonSettings())
|
||||||
|
app.store.dispatch(doFileList())
|
||||||
ReactDOM.render(<Provider store={store}><div>{ lbryio.enabled ? <AuthOverlay/> : '' }<App /><SnackBar /></div></Provider>, canvas)
|
ReactDOM.render(<Provider store={store}><div>{ lbryio.enabled ? <AuthOverlay/> : '' }<App /><SnackBar /></div></Provider>, canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,31 @@ import {
|
||||||
connect
|
connect
|
||||||
} from 'react-redux'
|
} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriTitle,
|
doFetchClaimsByChannel
|
||||||
} from 'selectors/app'
|
} from 'actions/content'
|
||||||
|
import {
|
||||||
|
makeSelectClaimForUri,
|
||||||
|
makeSelectClaimsInChannelForUri
|
||||||
|
} from 'selectors/claims'
|
||||||
import ChannelPage from './view'
|
import ChannelPage from './view'
|
||||||
|
|
||||||
const select = (state) => ({
|
import FilePage from './view'
|
||||||
title: selectCurrentUriTitle(state)
|
|
||||||
|
const makeSelect = () => {
|
||||||
|
const selectClaim = makeSelectClaimForUri(),
|
||||||
|
selectClaimsInChannel = makeSelectClaimsInChannelForUri()
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
claim: selectClaim(state, props),
|
||||||
|
claimsInChannel: selectClaimsInChannel(state, props)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return select
|
||||||
|
}
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
|
// fetchClaims: () => { console.log('fetch claims') }
|
||||||
|
fetchClaims: (uri) => dispatch(doFetchClaimsByChannel(uri))
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(select, perform)(ChannelPage)
|
export default connect(makeSelect, perform)(ChannelPage)
|
||||||
|
|
|
@ -1,14 +1,33 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import lbryuri from 'lbryuri'
|
||||||
|
|
||||||
const ChannelPage = (props) => {
|
class ChannelPage extends React.Component{
|
||||||
|
componentDidMount() {
|
||||||
|
this.fetchClaims(this.props)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.fetchClaims(nextProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchClaims(props) {
|
||||||
|
if (props.claimsInChannel === undefined) {
|
||||||
|
props.fetchClaims(props.uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
const {
|
const {
|
||||||
title
|
claimsInChannel,
|
||||||
} = props
|
claim,
|
||||||
|
uri
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
console.log(claimsInChannel);
|
||||||
return <main className="main--single-column">
|
return <main className="main--single-column">
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__inner">
|
<div className="card__inner">
|
||||||
<div className="card__title-identity"><h1>{title}</h1></div>
|
<div className="card__title-identity"><h1>{uri}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<p>
|
<p>
|
||||||
|
@ -16,7 +35,15 @@ const ChannelPage = (props) => {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<section className="card">
|
||||||
|
<div className="card__content">
|
||||||
|
{claimsInChannel ?
|
||||||
|
claimsInChannel.map((claim) => <FileTile uri={lbryuri.build({name: claim.name, claimId: claim.claim_id})} /> )
|
||||||
|
: ''}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default ChannelPage;
|
export default ChannelPage;
|
||||||
|
|
|
@ -3,13 +3,11 @@ import {
|
||||||
connect
|
connect
|
||||||
} from 'react-redux'
|
} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
doFetchDownloadedContent,
|
doFetchFileInfosAndPublishedClaims,
|
||||||
} from 'actions/content'
|
} from 'actions/file_info'
|
||||||
import {
|
import {
|
||||||
selectFetchingDownloadedContent,
|
selectFileInfosDownloaded,
|
||||||
} from 'selectors/content'
|
selectFileListDownloadedOrPublishedIsPending,
|
||||||
import {
|
|
||||||
selectDownloadedFileInfo,
|
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
import {
|
import {
|
||||||
doNavigate,
|
doNavigate,
|
||||||
|
@ -17,13 +15,13 @@ import {
|
||||||
import FileListDownloaded from './view'
|
import FileListDownloaded from './view'
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
downloadedContent: selectDownloadedFileInfo(state),
|
fileInfos: selectFileInfosDownloaded(state),
|
||||||
fetching: selectFetchingDownloadedContent(state),
|
isPending: selectFileListDownloadedOrPublishedIsPending(state),
|
||||||
})
|
})
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
navigate: (path) => dispatch(doNavigate(path)),
|
navigate: (path) => dispatch(doNavigate(path)),
|
||||||
fetchFileListDownloaded: () => dispatch(doFetchDownloadedContent()),
|
fetchFileInfosDownloaded: () => dispatch(doFetchFileInfosAndPublishedClaims()),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(select, perform)(FileListDownloaded)
|
export default connect(select, perform)(FileListDownloaded)
|
||||||
|
|
|
@ -12,23 +12,25 @@ import SubHeader from 'component/subHeader'
|
||||||
|
|
||||||
class FileListDownloaded extends React.Component {
|
class FileListDownloaded extends React.Component {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.props.fetchFileListDownloaded()
|
this.props.fetchFileInfosDownloaded()
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
downloadedContent,
|
fileInfos,
|
||||||
fetching,
|
isPending,
|
||||||
navigate,
|
navigate,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
let content
|
let content
|
||||||
if (fetching) {
|
if (fileInfos && fileInfos.length > 0) {
|
||||||
content = <BusyMessage message="Loading" />
|
content = <FileList fileInfos={fileInfos} fetching={isPending} />
|
||||||
} else if (!downloadedContent.length) {
|
|
||||||
content = <span>You haven't downloaded anything from LBRY yet. Go <Link onClick={() => navigate('/discover')} label="search for your first download" />!</span>
|
|
||||||
} else {
|
} else {
|
||||||
content = <FileList fileInfos={downloadedContent} hidePrices={true} />
|
if (isPending) {
|
||||||
|
content = <BusyMessage message="Loading" />
|
||||||
|
} else {
|
||||||
|
content = <span>You haven't downloaded anything from LBRY yet. Go <Link onClick={() => navigate('/discover')} label="search for your first download" />!</span>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3,13 +3,11 @@ import {
|
||||||
connect
|
connect
|
||||||
} from 'react-redux'
|
} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
doFetchPublishedContent,
|
doFetchFileInfosAndPublishedClaims,
|
||||||
} from 'actions/content'
|
} from 'actions/file_info'
|
||||||
import {
|
import {
|
||||||
selectFetchingPublishedContent,
|
selectFileInfosPublished,
|
||||||
} from 'selectors/content'
|
selectFileListDownloadedOrPublishedIsPending
|
||||||
import {
|
|
||||||
selectPublishedFileInfo,
|
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
import {
|
import {
|
||||||
doNavigate,
|
doNavigate,
|
||||||
|
@ -17,13 +15,13 @@ import {
|
||||||
import FileListPublished from './view'
|
import FileListPublished from './view'
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
publishedContent: selectPublishedFileInfo(state),
|
fileInfos: selectFileInfosPublished(state),
|
||||||
fetching: selectFetchingPublishedContent(state),
|
isPending: selectFileListDownloadedOrPublishedIsPending(state),
|
||||||
})
|
})
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
navigate: (path) => dispatch(doNavigate(path)),
|
navigate: (path) => dispatch(doNavigate(path)),
|
||||||
fetchFileListPublished: () => dispatch(doFetchPublishedContent()),
|
fetchFileListPublished: () => dispatch(doFetchFileInfosAndPublishedClaims()),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(select, perform)(FileListPublished)
|
export default connect(select, perform)(FileListPublished)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import lbry from 'lbry.js';
|
||||||
import lbryuri from 'lbryuri.js';
|
import lbryuri from 'lbryuri.js';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import {FormField} from 'component/form.js';
|
import {FormField} from 'component/form.js';
|
||||||
import {FileTile} from 'component/fileTile';
|
import FileTile from 'component/fileTile';
|
||||||
import rewards from 'rewards.js';
|
import rewards from 'rewards.js';
|
||||||
import lbryio from 'lbryio.js';
|
import lbryio from 'lbryio.js';
|
||||||
import {BusyMessage, Thumbnail} from 'component/common.js';
|
import {BusyMessage, Thumbnail} from 'component/common.js';
|
||||||
|
@ -16,7 +16,7 @@ class FileListPublished extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if(this.props.publishedContent.length > 0) this._requestPublishReward()
|
if(this.props.fileInfos.length > 0) this._requestPublishReward()
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestPublishReward() {
|
_requestPublishReward() {
|
||||||
|
@ -38,18 +38,21 @@ class FileListPublished extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
publishedContent,
|
fileInfos,
|
||||||
fetching,
|
isPending,
|
||||||
navigate,
|
navigate,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
let content
|
let content
|
||||||
if (fetching) {
|
|
||||||
content = <BusyMessage message="Loading" />
|
if (fileInfos && fileInfos.length > 0) {
|
||||||
} else if (!publishedContent.length) {
|
content = <FileList fileInfos={fileInfos} fetching={isPending} fileTileShowEmpty={FileTile.SHOW_EMPTY_PENDING} />
|
||||||
content = <span>You haven't downloaded anything from LBRY yet. Go <Link onClick={() => navigate('/discover')} label="search for your first download" />!</span>
|
|
||||||
} else {
|
} else {
|
||||||
content = <FileList fileInfos={publishedContent} hidePrices={true} />
|
if (isPending) {
|
||||||
|
content = <BusyMessage message="Loading" />
|
||||||
|
} else {
|
||||||
|
content = <span>You haven't downloaded anything from LBRY yet. Go <Link onClick={() => navigate('/discover')} label="search for your first download" />!</span>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3,29 +3,41 @@ import {
|
||||||
connect
|
connect
|
||||||
} from 'react-redux'
|
} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
selectCurrentUri,
|
doFetchFileInfo,
|
||||||
} from 'selectors/app'
|
} from 'actions/file_info'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriFileInfo,
|
makeSelectFileInfoForUri,
|
||||||
selectCurrentUriIsDownloaded,
|
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriClaim,
|
makeSelectClaimForUri,
|
||||||
|
makeSelectContentTypeForUri,
|
||||||
|
makeSelectMetadataForUri,
|
||||||
} from 'selectors/claims'
|
} from 'selectors/claims'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriCostInfo,
|
makeSelectCostInfoForUri,
|
||||||
} from 'selectors/cost_info'
|
} from 'selectors/cost_info'
|
||||||
import FilePage from './view'
|
import FilePage from './view'
|
||||||
|
|
||||||
const select = (state) => ({
|
const makeSelect = () => {
|
||||||
claim: selectCurrentUriClaim(state),
|
const selectClaim = makeSelectClaimForUri(),
|
||||||
uri: selectCurrentUri(state),
|
selectContentType = makeSelectContentTypeForUri(),
|
||||||
isDownloaded: selectCurrentUriIsDownloaded(state),
|
selectFileInfo = makeSelectFileInfoForUri(),
|
||||||
fileInfo: selectCurrentUriFileInfo(state),
|
selectCostInfo = makeSelectCostInfoForUri(),
|
||||||
costInfo: selectCurrentUriCostInfo(state),
|
selectMetadata = makeSelectMetadataForUri()
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
claim: selectClaim(state, props),
|
||||||
|
contentType: selectContentType(state, props),
|
||||||
|
costInfo: selectCostInfo(state, props),
|
||||||
|
metadata: selectMetadata(state, props),
|
||||||
|
fileInfo: selectFileInfo(state, props)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return select
|
||||||
|
}
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
|
fetchFileInfo: (uri) => dispatch(doFetchFileInfo(uri))
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(select, perform)(FilePage)
|
export default connect(makeSelect, perform)(FilePage)
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from 'lbry.js';
|
import lbry from 'lbry.js';
|
||||||
import lighthouse from 'lighthouse.js';
|
|
||||||
import lbryuri from 'lbryuri.js';
|
import lbryuri from 'lbryuri.js';
|
||||||
import Video from 'component/video'
|
import Video from 'component/video'
|
||||||
import {
|
import {
|
||||||
TruncatedText,
|
|
||||||
Thumbnail,
|
Thumbnail,
|
||||||
BusyMessage,
|
|
||||||
} from 'component/common';
|
} from 'component/common';
|
||||||
import FilePrice from 'component/filePrice'
|
import FilePrice from 'component/filePrice'
|
||||||
import FileActions from 'component/fileActions';
|
import FileActions from 'component/fileActions';
|
||||||
|
@ -45,80 +42,80 @@ const FormatItem = (props) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const FilePage = (props) => {
|
class FilePage extends React.Component{
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.fetchFileInfo(this.props)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.fetchFileInfo(nextProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchFileInfo(props) {
|
||||||
|
if (props.fileInfo === undefined) {
|
||||||
|
props.fetchFileInfo(props.uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
const {
|
const {
|
||||||
claim,
|
claim,
|
||||||
navigate,
|
fileInfo,
|
||||||
claim: {
|
metadata,
|
||||||
|
contentType,
|
||||||
|
uri,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
if (!claim || !metadata) {
|
||||||
|
return <span className="empty">Empty claim or metadata info.</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
txid,
|
txid,
|
||||||
nout,
|
nout,
|
||||||
has_signature: hasSignature,
|
has_signature: hasSignature,
|
||||||
signature_is_valid: signatureIsValid,
|
signature_is_valid: signatureIsValid,
|
||||||
value,
|
value
|
||||||
value: {
|
} = claim
|
||||||
stream,
|
|
||||||
stream: {
|
|
||||||
metadata,
|
|
||||||
source,
|
|
||||||
metadata: {
|
|
||||||
title,
|
|
||||||
} = {},
|
|
||||||
source: {
|
|
||||||
contentType,
|
|
||||||
} = {},
|
|
||||||
} = {},
|
|
||||||
} = {},
|
|
||||||
},
|
|
||||||
uri,
|
|
||||||
isDownloaded,
|
|
||||||
fileInfo,
|
|
||||||
costInfo,
|
|
||||||
costInfo: {
|
|
||||||
cost,
|
|
||||||
includesData: costIncludesData,
|
|
||||||
} = {},
|
|
||||||
} = props
|
|
||||||
|
|
||||||
const outpoint = txid + ':' + nout;
|
|
||||||
const uriLookupComplete = !!claim && Object.keys(claim).length
|
|
||||||
|
|
||||||
|
const outpoint = txid + ':' + nout
|
||||||
|
const title = metadata.title
|
||||||
const channelUriObj = lbryuri.parse(uri)
|
const channelUriObj = lbryuri.parse(uri)
|
||||||
delete channelUriObj.path;
|
delete channelUriObj.path;
|
||||||
delete channelUriObj.contentName;
|
delete channelUriObj.contentName;
|
||||||
const channelUri = signatureIsValid && hasSignature && channelUriObj.isChannel ? lbryuri.build(channelUriObj, false) : null;
|
const channelUri = signatureIsValid && hasSignature && channelUriObj.isChannel ? lbryuri.build(channelUriObj, false) : null
|
||||||
const uriIndicator = <UriIndicator uri={uri} />
|
const uriIndicator = <UriIndicator uri={uri} />
|
||||||
|
|
||||||
// <p>This location is not yet in use. { ' ' }<Link onClick={() => navigate('/publish')} label="Put something here" />.</p>
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<section className="show-page-media">
|
<section className="show-page-media">
|
||||||
{ contentType && contentType.startsWith('video/') ?
|
{ contentType && contentType.startsWith('video/') ?
|
||||||
<Video className="video-embedded" uri={uri} metadata={metadata} outpoint={outpoint} /> :
|
<Video className="video-embedded" uri={uri} /> :
|
||||||
(Object.keys(metadata).length > 0 ? <Thumbnail src={metadata.thumbnail} /> : <Thumbnail />) }
|
(metadata && metadata.thumbnail ? <Thumbnail src={metadata.thumbnail} /> : <Thumbnail />) }
|
||||||
</section>
|
</section>
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__inner">
|
<div className="card__inner">
|
||||||
<div className="card__title-identity">
|
<div className="card__title-identity">
|
||||||
{isDownloaded === false
|
{!fileInfo || fileInfo.written_bytes <= 0
|
||||||
? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(uri)} /></span>
|
? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(uri)} /></span>
|
||||||
: null}
|
: null}<h1>{title}</h1>
|
||||||
<h1>{title}</h1>
|
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
{ channelUri ?
|
{ channelUri ?
|
||||||
<Link href={"?show=" + channelUri }>{uriIndicator}</Link> :
|
<Link href={"?show=" + channelUri }>{uriIndicator}</Link> :
|
||||||
uriIndicator}
|
uriIndicator}
|
||||||
</div>
|
</div>
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<FileActions uri={uri} /></div>
|
<FileActions uri={uri} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
||||||
{metadata.description}
|
{metadata && metadata.description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{ metadata ?
|
{ metadata ?
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<FormatItem metadata={metadata} contentType={contentType} cost={cost} uri={uri} outpoint={outpoint} costIncludesData={costIncludesData} />
|
<FormatItem metadata={metadata} contentType={contentType} />
|
||||||
</div> : '' }
|
</div> : '' }
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
<Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
||||||
|
@ -127,5 +124,6 @@ const FilePage = (props) => {
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default FilePage;
|
export default FilePage;
|
|
@ -24,9 +24,6 @@ var HelpPage = React.createClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
|
||||||
document.title = "Help";
|
|
||||||
},
|
|
||||||
render: function() {
|
render: function() {
|
||||||
let ver, osName, platform, newVerLink;
|
let ver, osName, platform, newVerLink;
|
||||||
if (this.state.versionInfo) {
|
if (this.state.versionInfo) {
|
||||||
|
|
|
@ -5,9 +5,13 @@ import {
|
||||||
import {
|
import {
|
||||||
doNavigate,
|
doNavigate,
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
selectMyClaims
|
||||||
|
} from 'selectors/claims'
|
||||||
import PublishPage from './view'
|
import PublishPage from './view'
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
|
myClaims: selectMyClaims(state)
|
||||||
})
|
})
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from 'lbry';
|
import lbry from 'lbry';
|
||||||
|
import lbryuri from 'lbryuri'
|
||||||
import {FormField, FormRow} from 'component/form.js';
|
import {FormField, FormRow} from 'component/form.js';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import rewards from 'rewards';
|
import rewards from 'rewards';
|
||||||
|
@ -100,7 +101,7 @@ var PublishPage = React.createClass({
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.state.isFee) {
|
if (this.state.isFee) {
|
||||||
lbry.getUnusedAddress((address) => {
|
lbry.wallet_unused_address().then((address) => {
|
||||||
metadata.fee = {};
|
metadata.fee = {};
|
||||||
metadata.fee[this.state.feeCurrency] = {
|
metadata.fee[this.state.feeCurrency] = {
|
||||||
amount: parseFloat(this.state.feeAmount),
|
amount: parseFloat(this.state.feeAmount),
|
||||||
|
@ -169,7 +170,7 @@ var PublishPage = React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lbry.nameIsValid(rawName, false)) {
|
if (!lbryuri.isValidName(rawName, false)) {
|
||||||
this.refs.name.showError('LBRY names must contain only letters, numbers and dashes.');
|
this.refs.name.showError('LBRY names must contain only letters, numbers and dashes.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -182,11 +183,7 @@ var PublishPage = React.createClass({
|
||||||
myClaimExists: null,
|
myClaimExists: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
lbry.getMyClaim(name, (myClaimInfo) => {
|
const myClaimInfo = Object.values(this.props.myClaims).find(claim => claim.name === name)
|
||||||
if (name != this.state.name) {
|
|
||||||
// A new name has been typed already, so bail
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
myClaimExists: !!myClaimInfo,
|
myClaimExists: !!myClaimInfo,
|
||||||
|
@ -227,7 +224,6 @@ var PublishPage = React.createClass({
|
||||||
myClaimExists: false,
|
myClaimExists: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
handleBidChange: function(event) {
|
handleBidChange: function(event) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -287,7 +283,7 @@ var PublishPage = React.createClass({
|
||||||
handleNewChannelNameChange: function (event) {
|
handleNewChannelNameChange: function (event) {
|
||||||
const newChannelName = (event.target.value.startsWith('@') ? event.target.value : '@' + event.target.value);
|
const newChannelName = (event.target.value.startsWith('@') ? event.target.value : '@' + event.target.value);
|
||||||
|
|
||||||
if (newChannelName.length > 1 && !lbry.nameIsValid(newChannelName.substr(1), false)) {
|
if (newChannelName.length > 1 && !lbryuri.isValidName(newChannelName.substr(1), false)) {
|
||||||
this.refs.newChannelName.showError('LBRY channel names must contain only letters, numbers and dashes.');
|
this.refs.newChannelName.showError('LBRY channel names must contain only letters, numbers and dashes.');
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,14 +8,15 @@ import {BusyMessage} from 'component/common.js';
|
||||||
|
|
||||||
class SearchPage extends React.Component{
|
class SearchPage extends React.Component{
|
||||||
render() {
|
render() {
|
||||||
const isValidUri = (query) => true //FIXME
|
console.log('render search page')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
query,
|
query,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
{ isValidUri(query) ?
|
{ lbryuri.isValid(query) ?
|
||||||
<section className="section-spaced">
|
<section className="section-spaced">
|
||||||
<h3 className="card-row__header">
|
<h3 className="card-row__header">
|
||||||
Exact URL <ToolTip label="?" body="This is the resolution of a LBRY URL and not controlled by LBRY Inc."
|
Exact URL <ToolTip label="?" body="This is the resolution of a LBRY URL and not controlled by LBRY Inc."
|
||||||
|
|
21
ui/js/page/settings/index.js
Normal file
21
ui/js/page/settings/index.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
doSetDaemonSetting
|
||||||
|
} from 'actions/settings'
|
||||||
|
import {
|
||||||
|
selectDaemonSettings
|
||||||
|
} from 'selectors/settings'
|
||||||
|
import SettingsPage from './view'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
daemonSettings: selectDaemonSettings(state)
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(SettingsPage)
|
|
@ -1,19 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {FormField, FormRow} from '../component/form.js';
|
import {FormField, FormRow} from 'component/form.js';
|
||||||
import SubHeader from 'component/subHeader'
|
import SubHeader from 'component/subHeader'
|
||||||
import lbry from '../lbry.js';
|
import lbry from 'lbry.js';
|
||||||
|
|
||||||
var SettingsPage = React.createClass({
|
var SettingsPage = React.createClass({
|
||||||
_onSettingSaveSuccess: function() {
|
|
||||||
// This is bad.
|
|
||||||
// document.dispatchEvent(new CustomEvent('globalNotice', {
|
|
||||||
// detail: {
|
|
||||||
// message: "Settings saved",
|
|
||||||
// },
|
|
||||||
// }))
|
|
||||||
},
|
|
||||||
setDaemonSetting: function(name, value) {
|
setDaemonSetting: function(name, value) {
|
||||||
lbry.setDaemonSetting(name, value, this._onSettingSaveSuccess)
|
this.props.setDaemonSetting(name, value)
|
||||||
},
|
},
|
||||||
setClientSetting: function(name, value) {
|
setClientSetting: function(name, value) {
|
||||||
lbry.setClientSetting(name, value)
|
lbry.setClientSetting(name, value)
|
||||||
|
@ -51,21 +43,15 @@ var SettingsPage = React.createClass({
|
||||||
this.setDaemonSetting('max_download', Number(event.target.value));
|
this.setDaemonSetting('max_download', Number(event.target.value));
|
||||||
},
|
},
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
const daemonSettings = this.props.daemonSettings
|
||||||
|
|
||||||
return {
|
return {
|
||||||
settings: null,
|
isMaxUpload: daemonSettings && daemonSettings.max_upload != 0,
|
||||||
|
isMaxDownload: daemonSettings && daemonSettings.max_download != 0,
|
||||||
showNsfw: lbry.getClientSetting('showNsfw'),
|
showNsfw: lbry.getClientSetting('showNsfw'),
|
||||||
showUnavailable: lbry.getClientSetting('showUnavailable'),
|
showUnavailable: lbry.getClientSetting('showUnavailable'),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
componentWillMount: function() {
|
|
||||||
lbry.getDaemonSettings((settings) => {
|
|
||||||
this.setState({
|
|
||||||
daemonSettings: settings,
|
|
||||||
isMaxUpload: settings.max_upload != 0,
|
|
||||||
isMaxDownload: settings.max_download != 0
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onShowNsfwChange: function(event) {
|
onShowNsfwChange: function(event) {
|
||||||
lbry.setClientSetting('showNsfw', event.target.checked);
|
lbry.setClientSetting('showNsfw', event.target.checked);
|
||||||
},
|
},
|
||||||
|
@ -73,8 +59,12 @@ var SettingsPage = React.createClass({
|
||||||
|
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
if (!this.state.daemonSettings) {
|
const {
|
||||||
return null;
|
daemonSettings
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
if (!daemonSettings) {
|
||||||
|
return <main className="main--single-column"><span className="empty">Failed to load settings.</span></main>;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
<section className="card">
|
<section className="card">
|
||||||
|
@ -84,7 +74,7 @@ var SettingsPage = React.createClass({
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<FormRow type="checkbox"
|
<FormRow type="checkbox"
|
||||||
onChange={this.onRunOnStartChange}
|
onChange={this.onRunOnStartChange}
|
||||||
defaultChecked={this.state.daemonSettings.run_on_startup}
|
defaultChecked={daemonSettings.run_on_startup}
|
||||||
label="Run LBRY automatically when I start my computer" />
|
label="Run LBRY automatically when I start my computer" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -99,7 +89,7 @@ var SettingsPage = React.createClass({
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<FormRow type="directory"
|
<FormRow type="directory"
|
||||||
name="download_directory"
|
name="download_directory"
|
||||||
defaultValue={this.state.daemonSettings.download_directory}
|
defaultValue={daemonSettings.download_directory}
|
||||||
helper="LBRY downloads will be saved here."
|
helper="LBRY downloads will be saved here."
|
||||||
onChange={this.onDownloadDirChange} />
|
onChange={this.onDownloadDirChange} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,7 +115,7 @@ var SettingsPage = React.createClass({
|
||||||
<FormField type="number"
|
<FormField type="number"
|
||||||
min="0"
|
min="0"
|
||||||
step=".5"
|
step=".5"
|
||||||
defaultValue={this.state.daemonSettings.max_upload}
|
defaultValue={daemonSettings.max_upload}
|
||||||
placeholder="10"
|
placeholder="10"
|
||||||
className="form-field__input--inline"
|
className="form-field__input--inline"
|
||||||
onChange={this.onMaxUploadFieldChange}
|
onChange={this.onMaxUploadFieldChange}
|
||||||
|
@ -153,7 +143,7 @@ var SettingsPage = React.createClass({
|
||||||
<FormField type="number"
|
<FormField type="number"
|
||||||
min="0"
|
min="0"
|
||||||
step=".5"
|
step=".5"
|
||||||
defaultValue={this.state.daemonSettings.max_download}
|
defaultValue={daemonSettings.max_download}
|
||||||
placeholder="10"
|
placeholder="10"
|
||||||
className="form-field__input--inline"
|
className="form-field__input--inline"
|
||||||
onChange={this.onMaxDownloadFieldChange}
|
onChange={this.onMaxDownloadFieldChange}
|
||||||
|
@ -188,7 +178,7 @@ var SettingsPage = React.createClass({
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<FormRow type="checkbox"
|
<FormRow type="checkbox"
|
||||||
onChange={this.onShareDataChange}
|
onChange={this.onShareDataChange}
|
||||||
defaultChecked={this.state.daemonSettings.share_usage_data}
|
defaultChecked={daemonSettings.share_usage_data}
|
||||||
label="Help make LBRY better by contributing diagnostic data about my usage" />
|
label="Help make LBRY better by contributing diagnostic data about my usage" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
|
@ -6,24 +6,27 @@ import {
|
||||||
doResolveUri,
|
doResolveUri,
|
||||||
} from 'actions/content'
|
} from 'actions/content'
|
||||||
import {
|
import {
|
||||||
selectCurrentUri,
|
makeSelectClaimForUri,
|
||||||
} from 'selectors/app'
|
|
||||||
import {
|
|
||||||
selectCurrentUriClaim,
|
|
||||||
} from 'selectors/claims'
|
} from 'selectors/claims'
|
||||||
import {
|
import {
|
||||||
selectCurrentUriIsResolving,
|
makeSelectIsResolvingForUri,
|
||||||
} from 'selectors/content'
|
} from 'selectors/content'
|
||||||
import ShowPage from './view'
|
import ShowPage from './view'
|
||||||
|
|
||||||
|
const makeSelect = () => {
|
||||||
|
const selectClaim = makeSelectClaimForUri(),
|
||||||
|
selectIsResolving = makeSelectIsResolvingForUri();
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: selectCurrentUriClaim(state),
|
claim: selectClaim(state, props),
|
||||||
uri: selectCurrentUri(state),
|
isResolvingUri: selectIsResolving(state, props)
|
||||||
isResolvingUri: selectCurrentUriIsResolving(state)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return select
|
||||||
|
}
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
resolveUri: (uri) => dispatch(doResolveUri(uri))
|
resolveUri: (uri) => dispatch(doResolveUri(uri))
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(select, perform)(ShowPage)
|
export default connect(makeSelect, perform)(ShowPage)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import lbryuri from 'lbryuri'
|
||||||
import {
|
import {
|
||||||
BusyMessage,
|
BusyMessage,
|
||||||
} from 'component/common';
|
} from 'component/common';
|
||||||
|
import ChannelPage from 'page/channel'
|
||||||
import FilePage from 'page/filePage'
|
import FilePage from 'page/filePage'
|
||||||
|
|
||||||
class ShowPage extends React.Component{
|
class ShowPage extends React.Component{
|
||||||
|
@ -21,7 +23,7 @@ class ShowPage extends React.Component{
|
||||||
uri,
|
uri,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
if(!isResolvingUri && !claim && uri) {
|
if(!isResolvingUri && claim === undefined && uri) {
|
||||||
resolveUri(uri)
|
resolveUri(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,22 +37,22 @@ class ShowPage extends React.Component{
|
||||||
|
|
||||||
let innerContent = "";
|
let innerContent = "";
|
||||||
|
|
||||||
if (isResolvingUri) {
|
if (isResolvingUri || !claim) {
|
||||||
innerContent = <section className="card">
|
innerContent = <section className="card">
|
||||||
<div className="card__inner">
|
<div className="card__inner">
|
||||||
<div className="card__title-identity"><h1>{uri}</h1></div>
|
<div className="card__title-identity"><h1>{uri}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<BusyMessage message="Loading magic decentralized data..." /> :
|
{ isResolvingUri && <BusyMessage message="Loading magic decentralized data..." /> }
|
||||||
|
{ claim === null && <span className="empty">There's nothing at this location.</span> }
|
||||||
</div>
|
</div>
|
||||||
</section>;
|
</section>
|
||||||
}
|
}
|
||||||
else if (claim && claim.whatever) {
|
else if (claim.name.length && claim.name[0] === '@') {
|
||||||
innerContent = "channel"
|
innerContent = <ChannelPage uri={lbryuri.build({ name: claim.name, claimId: claim.claim_id })} />
|
||||||
// innerContent = <ChannelPage title={uri} />
|
|
||||||
}
|
}
|
||||||
else if (claim) {
|
else if (claim) {
|
||||||
innerContent = <FilePage />
|
innerContent = <FilePage uri={uri} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -9,7 +9,6 @@ const defaultState = {
|
||||||
upgradeSkipped: sessionStorage.getItem('upgradeSkipped'),
|
upgradeSkipped: sessionStorage.getItem('upgradeSkipped'),
|
||||||
daemonReady: false,
|
daemonReady: false,
|
||||||
obscureNsfw: !lbry.getClientSetting('showNsfw'),
|
obscureNsfw: !lbry.getClientSetting('showNsfw'),
|
||||||
hidePrice: false,
|
|
||||||
hasSignature: false,
|
hasSignature: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,8 @@ reducers[types.FETCH_AVAILABILITY_STARTED] = function(state, action) {
|
||||||
uri,
|
uri,
|
||||||
} = action.data
|
} = action.data
|
||||||
const newFetching = Object.assign({}, state.fetching)
|
const newFetching = Object.assign({}, state.fetching)
|
||||||
const newByUri = Object.assign({}, newFetching.byUri)
|
|
||||||
|
|
||||||
newByUri[uri] = true
|
newFetching[uri] = true
|
||||||
newFetching.byUri = newByUri
|
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
fetching: newFetching,
|
fetching: newFetching,
|
||||||
|
@ -24,12 +22,11 @@ reducers[types.FETCH_AVAILABILITY_COMPLETED] = function(state, action) {
|
||||||
uri,
|
uri,
|
||||||
availability,
|
availability,
|
||||||
} = action.data
|
} = action.data
|
||||||
|
|
||||||
const newFetching = Object.assign({}, state.fetching)
|
const newFetching = Object.assign({}, state.fetching)
|
||||||
const newFetchingByUri = Object.assign({}, newFetching.byUri)
|
|
||||||
const newAvailabilityByUri = Object.assign({}, state.byUri)
|
const newAvailabilityByUri = Object.assign({}, state.byUri)
|
||||||
|
|
||||||
delete newFetchingByUri[uri]
|
delete newFetching[uri]
|
||||||
newFetching.byUri = newFetchingByUri
|
|
||||||
newAvailabilityByUri[uri] = availability
|
newAvailabilityByUri[uri] = availability
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
|
|
|
@ -8,30 +8,69 @@ const defaultState = {
|
||||||
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
uri,
|
||||||
|
certificate,
|
||||||
claim,
|
claim,
|
||||||
} = action.data
|
} = 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, {
|
return Object.assign({}, state, {
|
||||||
byUri: newByUri,
|
claimsByUri: newClaims
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
reducers[types.FETCH_MY_CLAIMS_COMPLETED] = function(state, action) {
|
reducers[types.RESOLVE_URI_CANCELED] = function(state, action) {
|
||||||
const {
|
const uri = action.data.uri
|
||||||
claims,
|
const newClaims = Object.assign({}, state.claimsByUri)
|
||||||
} = action.data
|
delete newClaims[uri]
|
||||||
const newMine = Object.assign({}, state.mine)
|
return Object.assign({}, state, {
|
||||||
const newById = Object.assign({}, newMine.byId)
|
claimsByUri: newClaims
|
||||||
|
|
||||||
claims.forEach(claim => {
|
|
||||||
newById[claim.claim_id] = claim
|
|
||||||
})
|
})
|
||||||
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, {
|
return Object.assign({}, state, {
|
||||||
mine: newMine,
|
claimsByChannel: newClaims
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ reducers[types.RESOLVE_URI_STARTED] = function(state, action) {
|
||||||
|
|
||||||
const oldResolving = state.resolvingUris || []
|
const oldResolving = state.resolvingUris || []
|
||||||
const newResolving = Object.assign([], oldResolving)
|
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, {
|
return Object.assign({}, state, {
|
||||||
resolvingUris: newResolving
|
resolvingUris: newResolving
|
||||||
|
@ -48,51 +48,14 @@ reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
||||||
...resolvingUris.slice(index + 1)
|
...resolvingUris.slice(index + 1)
|
||||||
]
|
]
|
||||||
|
|
||||||
const newState = Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
resolvingUris: newResolvingUris,
|
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) {
|
reducers[types.RESOLVE_URI_CANCELED] = function(state, action) {
|
||||||
const {
|
return reducers[types.RESOLVE_URI_COMPLETED](state, action)
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function reducer(state = defaultState, action) {
|
export default function reducer(state = defaultState, action) {
|
||||||
|
|
|
@ -5,13 +5,35 @@ const reducers = {}
|
||||||
const defaultState = {
|
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) {
|
reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
outpoint
|
||||||
} = action.data
|
} = action.data
|
||||||
const newFetching = Object.assign({}, state.fetching)
|
const newFetching = Object.assign({}, state.fetching)
|
||||||
|
|
||||||
newFetching[uri] = true
|
newFetching[outpoint] = true
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
fetching: newFetching,
|
fetching: newFetching,
|
||||||
|
@ -20,17 +42,18 @@ reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) {
|
||||||
|
|
||||||
reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
|
reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
|
||||||
fileInfo,
|
fileInfo,
|
||||||
|
outpoint,
|
||||||
} = action.data
|
} = action.data
|
||||||
const newByUri = Object.assign({}, state.byUri)
|
|
||||||
|
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||||
const newFetching = Object.assign({}, state.fetching)
|
const newFetching = Object.assign({}, state.fetching)
|
||||||
|
|
||||||
newByUri[uri] = fileInfo || {}
|
newFileInfos[outpoint] = fileInfo
|
||||||
delete newFetching[uri]
|
delete newFetching[outpoint]
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
byUri: newByUri,
|
fileInfos: newFileInfos,
|
||||||
fetching: newFetching,
|
fetching: newFetching,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -38,93 +61,74 @@ reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
|
||||||
reducers[types.DOWNLOADING_STARTED] = function(state, action) {
|
reducers[types.DOWNLOADING_STARTED] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
uri,
|
||||||
|
outpoint,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
} = action.data
|
} = 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
|
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||||
newDownloading.byUri = newDownloadingByUri
|
const newDownloading = Object.assign({}, state.urisDownloading)
|
||||||
newByUri[uri] = fileInfo
|
const newLoading = Object.assign({}, state.urisLoading)
|
||||||
delete newLoadingByUri[uri]
|
|
||||||
newLoading.byUri = newLoadingByUri
|
newDownloading[uri] = true
|
||||||
|
newFileInfos[outpoint] = fileInfo
|
||||||
|
delete newLoading[uri]
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
downloading: newDownloading,
|
urisDownloading: newDownloading,
|
||||||
byUri: newByUri,
|
urisLoading: newLoading,
|
||||||
loading: newLoading,
|
fileInfos: newFileInfos,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) {
|
reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
uri,
|
||||||
|
outpoint,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
} = action.data
|
} = 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
|
newDownloading[uri] = true
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
byUri: newByUri,
|
fileInfos: newFileInfos,
|
||||||
downloading: newDownloading
|
urisDownloading: newDownloading
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
|
reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
uri,
|
||||||
|
outpoint,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
} = action.data
|
} = action.data
|
||||||
const newByUri = Object.assign({}, state.byUri)
|
|
||||||
const newDownloading = Object.assign({}, state.downloading)
|
|
||||||
const newDownloadingByUri = Object.assign({}, newDownloading.byUri)
|
|
||||||
|
|
||||||
newByUri[uri] = fileInfo
|
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||||
delete newDownloadingByUri[uri]
|
const newDownloading = Object.assign({}, state.urisDownloading)
|
||||||
newDownloading.byUri = newDownloadingByUri
|
|
||||||
|
newFileInfos[outpoint] = fileInfo
|
||||||
|
delete newDownloading[uri]
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
byUri: newByUri,
|
fileInfos: newFileInfos,
|
||||||
downloading: newDownloading,
|
urisDownloading: newDownloading,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
reducers[types.DELETE_FILE_STARTED] = function(state, action) {
|
reducers[types.FILE_DELETE] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
outpoint,
|
||||||
} = action.data
|
} = action.data
|
||||||
const newDeleting = Object.assign({}, state.deleting)
|
|
||||||
const newByUri = Object.assign({}, newDeleting.byUri)
|
|
||||||
|
|
||||||
newByUri[uri] = true
|
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||||
newDeleting.byUri = newByUri
|
|
||||||
|
delete newFileInfos[outpoint]
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
deleting: newDeleting,
|
fileInfos: newFileInfos,
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,14 +136,13 @@ reducers[types.LOADING_VIDEO_STARTED] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
uri,
|
||||||
} = action.data
|
} = action.data
|
||||||
const newLoading = Object.assign({}, state.loading)
|
|
||||||
const newByUri = Object.assign({}, newLoading.byUri)
|
|
||||||
|
|
||||||
newByUri[uri] = true
|
const newLoading = Object.assign({}, state.urisLoading)
|
||||||
newLoading.byUri = newByUri
|
|
||||||
|
newLoading[uri] = true
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
loading: newLoading,
|
urisLoading: newLoading,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,54 +150,13 @@ reducers[types.LOADING_VIDEO_FAILED] = function(state, action) {
|
||||||
const {
|
const {
|
||||||
uri,
|
uri,
|
||||||
} = action.data
|
} = action.data
|
||||||
const newLoading = Object.assign({}, state.loading)
|
|
||||||
const newByUri = Object.assign({}, newLoading.byUri)
|
|
||||||
|
|
||||||
delete newByUri[uri]
|
const newLoading = Object.assign({}, state.urisLoading)
|
||||||
newLoading.byUri = newByUri
|
|
||||||
|
delete newLoading[uri]
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
loading: newLoading,
|
urisLoading: 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
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,14 @@
|
||||||
import * as types from 'constants/action_types'
|
import * as types from 'constants/action_types'
|
||||||
|
|
||||||
const reducers = {}
|
const reducers = {}
|
||||||
const defaultState = {
|
const defaultState = {}
|
||||||
}
|
|
||||||
|
|
||||||
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
reducers[types.DAEMON_SETTINGS_RECEIVED] = function(state, action) {
|
||||||
const {
|
|
||||||
uri,
|
|
||||||
certificate,
|
|
||||||
} = action.data
|
|
||||||
if (!certificate) return state
|
|
||||||
|
|
||||||
const newByUri = Object.assign({}, state.byUri)
|
|
||||||
|
|
||||||
newByUri[uri] = certificate
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
byUri: newByUri,
|
daemonSettings: action.data.settings
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function reducer(state = defaultState, action) {
|
export default function reducer(state = defaultState, action) {
|
||||||
const handler = reducers[action.type];
|
const handler = reducers[action.type];
|
||||||
if (handler) return handler(state, action);
|
if (handler) return handler(state, action);
|
|
@ -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(
|
export const selectPageTitle = createSelector(
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentUri,
|
selectCurrentParams,
|
||||||
(page, uri) => {
|
(page, params) => {
|
||||||
switch (page) {
|
switch (page) {
|
||||||
case 'search':
|
case 'search':
|
||||||
return 'Search'
|
return 'Search'
|
||||||
|
@ -67,7 +50,7 @@ export const selectPageTitle = createSelector(
|
||||||
case 'rewards':
|
case 'rewards':
|
||||||
return page.charAt(0).toUpperCase() + page.slice(1)
|
return page.charAt(0).toUpperCase() + page.slice(1)
|
||||||
case 'show':
|
case 'show':
|
||||||
return lbryuri.normalize(uri)
|
return lbryuri.normalize(params.uri)
|
||||||
case 'downloaded':
|
case 'downloaded':
|
||||||
return 'Downloads & Purchases'
|
return 'Downloads & Purchases'
|
||||||
case 'published':
|
case 'published':
|
||||||
|
@ -195,11 +178,6 @@ export const selectUpgradeDownloadItem = createSelector(
|
||||||
(state) => state.downloadItem
|
(state) => state.downloadItem
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectSearchTerm = createSelector(
|
|
||||||
_selectState,
|
|
||||||
(state) => state.searchTerm
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectError = createSelector(
|
export const selectError = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => state.error
|
(state) => state.error
|
||||||
|
@ -214,13 +192,3 @@ export const selectObscureNsfw = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => !!state.obscureNsfw
|
(state) => !!state.obscureNsfw
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectHidePrice = createSelector(
|
|
||||||
_selectState,
|
|
||||||
(state) => !!state.hidePrice
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectHasSignature = createSelector(
|
|
||||||
_selectState,
|
|
||||||
(state) => !!state.hasSignature
|
|
||||||
)
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
import {
|
import {
|
||||||
selectDaemonReady,
|
selectDaemonReady,
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentUri,
|
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
|
|
||||||
const _selectState = state => state.availability
|
const _selectState = state => state.availability
|
||||||
|
@ -14,29 +13,24 @@ export const selectAvailabilityByUri = createSelector(
|
||||||
(state) => state.byUri || {}
|
(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(
|
export const selectFetchingAvailability = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => state.fetching || {}
|
(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) => {
|
const selectFetchingAvailabilityForUri = (state, props) => {
|
||||||
return selectFetchingAvailabilityByUri(state)[props.uri]
|
return selectFetchingAvailability(state)[props.uri]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeSelectFetchingAvailabilityForUri = () => {
|
export const makeSelectFetchingAvailabilityForUri = () => {
|
||||||
|
@ -45,15 +39,3 @@ export const makeSelectFetchingAvailabilityForUri = () => {
|
||||||
(fetching) => fetching
|
(fetching) => fetching
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const selectFetchingAvailabilityForCurrentUri = createSelector(
|
|
||||||
selectCurrentUri,
|
|
||||||
selectFetchingAvailabilityByUri,
|
|
||||||
(uri, byUri) => byUri[uri]
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectAvailabilityForCurrentUri = createSelector(
|
|
||||||
selectCurrentUri,
|
|
||||||
selectAvailabilityByUri,
|
|
||||||
(uri, byUri) => byUri[uri]
|
|
||||||
)
|
|
|
@ -2,28 +2,17 @@ import {
|
||||||
createSelector,
|
createSelector,
|
||||||
} from 'reselect'
|
} from 'reselect'
|
||||||
import lbryuri from 'lbryuri'
|
import lbryuri from 'lbryuri'
|
||||||
import {
|
|
||||||
selectCurrentUri,
|
|
||||||
} from 'selectors/app'
|
|
||||||
|
|
||||||
export const _selectState = state => state.claims || {}
|
export const _selectState = state => state.claims || {}
|
||||||
|
|
||||||
export const selectClaimsByUri = createSelector(
|
export const selectClaimsByUri = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => state.byUri || {}
|
(state) => state.claimsByUri || {}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectCurrentUriClaim = createSelector(
|
export const selectAllClaimsByChannel = createSelector(
|
||||||
selectCurrentUri,
|
_selectState,
|
||||||
selectClaimsByUri,
|
(state) => state.claimsByChannel || {}
|
||||||
(uri, byUri) => byUri[uri]
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectCurrentUriClaimOutpoint = createSelector(
|
|
||||||
selectCurrentUriClaim,
|
|
||||||
(claim) => {
|
|
||||||
return claim ? `${claim.txid}:${claim.nout}` : null
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectClaimForUri = (state, props) => {
|
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 selectMetadataForUri = (state, props) => {
|
||||||
const claim = selectClaimForUri(state, props)
|
const claim = selectClaimForUri(state, props)
|
||||||
const metadata = claim && claim.value && claim.value.stream && claim.value.stream.metadata
|
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 = () => {
|
export const makeSelectMetadataForUri = () => {
|
||||||
|
@ -56,7 +56,7 @@ const selectSourceForUri = (state, props) => {
|
||||||
const claim = selectClaimForUri(state, props)
|
const claim = selectClaimForUri(state, props)
|
||||||
const source = claim && claim.value && claim.value.stream && claim.value.stream.source
|
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 = () => {
|
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,
|
_selectState,
|
||||||
(state) => state.mine || {}
|
(state) => state.isClaimListMinePending
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectMyClaimsById = createSelector(
|
export const selectMyClaims = createSelector(
|
||||||
selectMyClaims,
|
_selectState,
|
||||||
(mine) => mine.byId || {}
|
(state) => state.myClaims || {}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectMyClaimsOutpoints = createSelector(
|
export const selectMyClaimsOutpoints = createSelector(
|
||||||
selectMyClaimsById,
|
selectMyClaims,
|
||||||
(byId) => {
|
(claims) => {
|
||||||
const outpoints = []
|
if (!claims) {
|
||||||
Object.keys(byId).forEach(key => {
|
return []
|
||||||
const claim = byId[key]
|
}
|
||||||
const outpoint = `${claim.txid}:${claim.nout}`
|
|
||||||
outpoints.push(outpoint)
|
|
||||||
})
|
|
||||||
|
|
||||||
return outpoints
|
return Object.values(claims).map((claim) => {
|
||||||
|
return `${claim.txid}:${claim.nout}`
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { createSelector } from 'reselect'
|
||||||
import {
|
import {
|
||||||
selectDaemonReady,
|
selectDaemonReady,
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentUri,
|
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
|
|
||||||
export const _selectState = state => state.content || {}
|
export const _selectState = state => state.content || {}
|
||||||
|
@ -17,54 +16,16 @@ export const selectFetchingFeaturedUris = createSelector(
|
||||||
(state) => !!state.fetchingFeaturedContent
|
(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(
|
export const selectResolvingUris = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => state.resolvingUris || []
|
(state) => state.resolvingUris || []
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
export const selectCurrentUriIsResolving = createSelector(
|
|
||||||
selectCurrentUri,
|
|
||||||
selectResolvingUris,
|
|
||||||
(uri, resolvingUris) => resolvingUris.indexOf(uri) != -1
|
|
||||||
)
|
|
||||||
|
|
||||||
const selectResolvingUri = (state, props) => {
|
const selectResolvingUri = (state, props) => {
|
||||||
return selectResolvingUris(state).indexOf(props.uri) != -1
|
return selectResolvingUris(state).indexOf(props.uri) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeSelectResolvingUri = () => {
|
export const makeSelectIsResolvingForUri = () => {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
selectResolvingUri,
|
selectResolvingUri,
|
||||||
(resolving) => resolving
|
(resolving) => resolving
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import { createSelector } from 'reselect'
|
import { createSelector } from 'reselect'
|
||||||
import {
|
|
||||||
selectCurrentUri,
|
|
||||||
selectCurrentPage,
|
|
||||||
} from 'selectors/app'
|
|
||||||
|
|
||||||
export const _selectState = state => state.costInfo || {}
|
export const _selectState = state => state.costInfo || {}
|
||||||
|
|
||||||
|
@ -11,24 +7,7 @@ export const selectAllCostInfoByUri = createSelector(
|
||||||
(state) => state.byUri || {}
|
(state) => state.byUri || {}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectCurrentUriCostInfo = createSelector(
|
export const selectCostInfoForUri = (state, props) => {
|
||||||
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) => {
|
|
||||||
return selectAllCostInfoByUri(state)[props.uri]
|
return selectAllCostInfoByUri(state)[props.uri]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,72 +1,38 @@
|
||||||
|
import lbry from 'lbry'
|
||||||
import {
|
import {
|
||||||
createSelector,
|
createSelector,
|
||||||
} from 'reselect'
|
} from 'reselect'
|
||||||
import {
|
import {
|
||||||
selectCurrentUri,
|
selectClaimsByUri,
|
||||||
selectCurrentPage,
|
selectClaimListMineIsPending,
|
||||||
} from 'selectors/app'
|
|
||||||
import {
|
|
||||||
selectMyClaimsOutpoints,
|
selectMyClaimsOutpoints,
|
||||||
} from 'selectors/claims'
|
} from 'selectors/claims'
|
||||||
|
|
||||||
export const _selectState = state => state.fileInfo || {}
|
export const _selectState = state => state.fileInfo || {}
|
||||||
|
|
||||||
export const selectAllFileInfoByUri = createSelector(
|
export const selectAllFileInfos = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => state.byUri || {}
|
(state) => state.fileInfos || {}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectCurrentUriRawFileInfo = createSelector(
|
export const selectFileListIsPending = createSelector(
|
||||||
selectCurrentUri,
|
|
||||||
selectAllFileInfoByUri,
|
|
||||||
(uri, byUri) => byUri[uri]
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectCurrentUriFileInfo = createSelector(
|
|
||||||
selectCurrentUriRawFileInfo,
|
|
||||||
(fileInfo) => fileInfo
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectFetchingFileInfo = createSelector(
|
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => state.fetching || {}
|
(state) => state.isFileListPending
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectFetchingCurrentUriFileInfo = createSelector(
|
export const selectFileListDownloadedOrPublishedIsPending = createSelector(
|
||||||
selectCurrentUri,
|
selectFileListIsPending,
|
||||||
selectFetchingFileInfo,
|
selectClaimListMineIsPending,
|
||||||
(uri, byUri) => !!byUri[uri]
|
(isFileListPending, isClaimListMinePending) => isFileListPending || isClaimListMinePending
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectDownloading = createSelector(
|
export const selectFileInfoForUri = (state, props) => {
|
||||||
_selectState,
|
const claims = selectClaimsByUri(state),
|
||||||
(state) => state.downloading || {}
|
claim = claims[props.uri],
|
||||||
)
|
fileInfos = selectAllFileInfos(state),
|
||||||
|
outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined
|
||||||
|
|
||||||
export const selectDownloadingByUri = createSelector(
|
return outpoint && fileInfos ? fileInfos[outpoint] : undefined
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeSelectFileInfoForUri = () => {
|
export const makeSelectFileInfoForUri = () => {
|
||||||
|
@ -76,8 +42,13 @@ export const makeSelectFileInfoForUri = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const selectUrisDownloading = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.urisDownloading || {}
|
||||||
|
)
|
||||||
|
|
||||||
const selectDownloadingForUri = (state, props) => {
|
const selectDownloadingForUri = (state, props) => {
|
||||||
const byUri = selectDownloadingByUri(state)
|
const byUri = selectUrisDownloading(state)
|
||||||
return byUri[props.uri]
|
return byUri[props.uri]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,31 +59,13 @@ export const makeSelectDownloadingForUri = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const selectLoading = createSelector(
|
export const selectUrisLoading = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => state.loading || {}
|
(state) => state.urisLoading || {}
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectLoadingForUri = (state, props) => {
|
const selectLoadingForUri = (state, props) => {
|
||||||
const byUri = selectLoadingByUri(state)
|
const byUri = selectUrisLoading(state)
|
||||||
return byUri[props.uri]
|
return byUri[props.uri]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,36 +76,38 @@ export const makeSelectLoadingForUri = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const selectDownloadedFileInfo = createSelector(
|
export const selectFileInfosDownloaded = createSelector(
|
||||||
selectAllFileInfoByUri,
|
selectAllFileInfos,
|
||||||
(byUri) => {
|
selectMyClaimsOutpoints,
|
||||||
|
(fileInfos, myClaimOutpoints) => {
|
||||||
const fileInfoList = []
|
const fileInfoList = []
|
||||||
Object.keys(byUri).forEach(key => {
|
Object.values(fileInfos).forEach(fileInfo => {
|
||||||
const fileInfo = byUri[key]
|
if (fileInfo && myClaimOutpoints.indexOf(fileInfo.outpoint) === -1 && (fileInfo.completed || fileInfo.written_bytes)) {
|
||||||
|
|
||||||
if (fileInfo.completed || fileInfo.written_bytes) {
|
|
||||||
fileInfoList.push(fileInfo)
|
fileInfoList.push(fileInfo)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return fileInfoList
|
return fileInfoList
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectPublishedFileInfo = createSelector(
|
export const selectFileInfosPendingPublish = createSelector(
|
||||||
selectAllFileInfoByUri,
|
_selectState,
|
||||||
selectMyClaimsOutpoints,
|
(state) => {
|
||||||
(byUri, outpoints) => {
|
return lbry.getPendingPublishes()
|
||||||
const fileInfos = []
|
}
|
||||||
outpoints.forEach(outpoint => {
|
)
|
||||||
Object.keys(byUri).forEach(key => {
|
|
||||||
const fileInfo = byUri[key]
|
export const selectFileInfosPublished = createSelector(
|
||||||
if (fileInfo.outpoint == outpoint) {
|
selectAllFileInfos,
|
||||||
fileInfos.push(fileInfo)
|
selectFileInfosPendingPublish,
|
||||||
}
|
selectMyClaimsOutpoints,
|
||||||
})
|
(allFileInfos, pendingFileInfos, outpoints) => {
|
||||||
})
|
const fileInfos = []
|
||||||
|
outpoints.forEach(outpoint => {
|
||||||
return fileInfos
|
if (allFileInfos[outpoint]) {
|
||||||
|
fileInfos.push(allFileInfos[outpoint])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return [...fileInfos, ...pendingFileInfos]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { createSelector } from 'reselect'
|
||||||
import {
|
import {
|
||||||
selectPageTitle,
|
selectPageTitle,
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentUri
|
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
|
|
||||||
export const _selectState = state => state.search || {}
|
export const _selectState = state => state.search || {}
|
||||||
|
@ -43,8 +42,7 @@ export const selectWunderBarAddress = createSelector(
|
||||||
|
|
||||||
export const selectWunderBarIcon = createSelector(
|
export const selectWunderBarIcon = createSelector(
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentUri,
|
(page) => {
|
||||||
(page, uri) => {
|
|
||||||
switch (page) {
|
switch (page) {
|
||||||
case 'search':
|
case 'search':
|
||||||
return 'icon-search'
|
return 'icon-search'
|
||||||
|
|
20
ui/js/selectors/settings.js
Normal file
20
ui/js/selectors/settings.js
Normal file
|
@ -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
|
||||||
|
)
|
|
@ -7,13 +7,13 @@ import {
|
||||||
} from 'redux-logger'
|
} from 'redux-logger'
|
||||||
import appReducer from 'reducers/app';
|
import appReducer from 'reducers/app';
|
||||||
import availabilityReducer from 'reducers/availability'
|
import availabilityReducer from 'reducers/availability'
|
||||||
import certificatesReducer from 'reducers/certificates'
|
|
||||||
import claimsReducer from 'reducers/claims'
|
import claimsReducer from 'reducers/claims'
|
||||||
import contentReducer from 'reducers/content';
|
import contentReducer from 'reducers/content';
|
||||||
import costInfoReducer from 'reducers/cost_info'
|
import costInfoReducer from 'reducers/cost_info'
|
||||||
import fileInfoReducer from 'reducers/file_info'
|
import fileInfoReducer from 'reducers/file_info'
|
||||||
import rewardsReducer from 'reducers/rewards'
|
import rewardsReducer from 'reducers/rewards'
|
||||||
import searchReducer from 'reducers/search'
|
import searchReducer from 'reducers/search'
|
||||||
|
import settingsReducer from 'reducers/settings'
|
||||||
import walletReducer from 'reducers/wallet'
|
import walletReducer from 'reducers/wallet'
|
||||||
|
|
||||||
function isFunction(object) {
|
function isFunction(object) {
|
||||||
|
@ -49,13 +49,13 @@ function enableBatching(reducer) {
|
||||||
const reducers = redux.combineReducers({
|
const reducers = redux.combineReducers({
|
||||||
app: appReducer,
|
app: appReducer,
|
||||||
availability: availabilityReducer,
|
availability: availabilityReducer,
|
||||||
certificates: certificatesReducer,
|
|
||||||
claims: claimsReducer,
|
claims: claimsReducer,
|
||||||
fileInfo: fileInfoReducer,
|
fileInfo: fileInfoReducer,
|
||||||
content: contentReducer,
|
content: contentReducer,
|
||||||
costInfo: costInfoReducer,
|
costInfo: costInfoReducer,
|
||||||
rewards: rewardsReducer,
|
rewards: rewardsReducer,
|
||||||
search: searchReducer,
|
search: searchReducer,
|
||||||
|
settings: settingsReducer,
|
||||||
wallet: walletReducer,
|
wallet: walletReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
"babel-cli": "^6.11.4",
|
"babel-cli": "^6.11.4",
|
||||||
"babel-preset-es2015": "^6.13.2",
|
"babel-preset-es2015": "^6.13.2",
|
||||||
"babel-preset-react": "^6.11.1",
|
"babel-preset-react": "^6.11.1",
|
||||||
"mediaelement": "^2.23.4",
|
|
||||||
"node-sass": "^3.8.0",
|
"node-sass": "^3.8.0",
|
||||||
"plyr": "^2.0.12",
|
"plyr": "^2.0.12",
|
||||||
"rc-progress": "^2.0.6",
|
"rc-progress": "^2.0.6",
|
||||||
|
|
|
@ -143,6 +143,14 @@ p
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*should be redone/moved*/
|
||||||
|
.file-list__header {
|
||||||
|
.busy-indicator {
|
||||||
|
float: left;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sort-section {
|
.sort-section {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: $spacing-vertical * 2/3;
|
margin-bottom: $spacing-vertical * 2/3;
|
||||||
|
|
|
@ -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%;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
@import "_reset";
|
@import "_reset";
|
||||||
@import "_icons";
|
@import "_icons";
|
||||||
@import "_mediaelement";
|
|
||||||
@import "_gui";
|
@import "_gui";
|
||||||
@import "component/_table";
|
@import "component/_table";
|
||||||
@import "component/_button.scss";
|
@import "component/_button.scss";
|
||||||
|
@ -20,6 +19,5 @@
|
||||||
@import "component/_snack-bar.scss";
|
@import "component/_snack-bar.scss";
|
||||||
@import "component/_video.scss";
|
@import "component/_video.scss";
|
||||||
@import "page/_developer.scss";
|
@import "page/_developer.scss";
|
||||||
@import "page/_watch.scss";
|
|
||||||
@import "page/_reward.scss";
|
@import "page/_reward.scss";
|
||||||
@import "page/_show.scss";
|
@import "page/_show.scss";
|
||||||
|
|
|
@ -3,6 +3,9 @@ video {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video {
|
.video {
|
||||||
|
@ -15,6 +18,7 @@ video {
|
||||||
max-width: $width-page-constrained;
|
max-width: $width-page-constrained;
|
||||||
max-height: $height-video-embedded;
|
max-height: $height-video-embedded;
|
||||||
height: $height-video-embedded;
|
height: $height-video-embedded;
|
||||||
|
position: relative; /*for .plyr below*/
|
||||||
video {
|
video {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +28,10 @@ video {
|
||||||
&.video--active {
|
&.video--active {
|
||||||
/*background: none;*/
|
/*background: none;*/
|
||||||
}
|
}
|
||||||
|
.plyr {
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video__cover {
|
.video__cover {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
Loading…
Reference in a new issue