Merge branch 'redux' into file-selector
This commit is contained in:
commit
f60d17f604
73 changed files with 1328 additions and 1503 deletions
CHANGELOG.mdpackage.json
app
lbrylbryschemalbryumui
js
actions
component
constants
jsonrpc.jslbry.jslbryio.jslbryuri.jsmain.jspage
channel
fileListDownloaded
fileListPublished
filePage
help
publish
search
settings
showPage
reducers
selectors
store.jsscss
|
@ -55,6 +55,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
|||
* Handle more of price calculations at the daemon layer to improve page load time
|
||||
* Add special support for building channel claims in lbryuri module
|
||||
* Enable windows code signing of binary
|
||||
* Support for opening LBRY URIs from links in other apps
|
||||
|
||||
|
||||
### Changed
|
||||
|
@ -66,6 +67,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
|||
|
||||
### Fixed
|
||||
* Fix Watch page and progress bars for new API changes
|
||||
* On Windows, prevent opening multiple LBRY instances (launching LBRY again just focuses the current instance)
|
||||
|
||||
|
||||
|
||||
|
|
50
app/main.js
50
app/main.js
|
@ -37,6 +37,22 @@ let readyToQuit = false;
|
|||
// send it to, it's cached in this variable.
|
||||
let openUri = null;
|
||||
|
||||
function denormalizeUri(uri) {
|
||||
// Windows normalizes URIs when they're passed in from other apps. This tries
|
||||
// to restore the original URI that was typed.
|
||||
// - If the URI has no path, Windows adds a trailing slash. LBRY URIs
|
||||
// can't have a slash with no path, so we just strip it off.
|
||||
// - In a URI with a claim ID, like lbry://channel#claimid, Windows
|
||||
// interprets the hash mark as an anchor and converts it to
|
||||
// lbry://channel/#claimid. We remove the slash here as well.
|
||||
|
||||
if (process.platform == 'win32') {
|
||||
return uri.replace(/\/$/, '').replace('/#', '#');
|
||||
} else {
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
||||
function checkForNewVersion(callback) {
|
||||
function formatRc(ver) {
|
||||
// Adds dash if needed to make RC suffix semver friendly
|
||||
|
@ -186,6 +202,29 @@ function quitNow() {
|
|||
app.quit();
|
||||
}
|
||||
|
||||
if (process.platform != 'linux') {
|
||||
// On Linux, this is always returning true due to an Electron bug,
|
||||
// so for now we just don't support single-instance apps on Linux.
|
||||
const isSecondaryInstance = app.makeSingleInstance((argv) => {
|
||||
if (win) {
|
||||
if (win.isMinimized()) {
|
||||
win.restore();
|
||||
}
|
||||
win.focus();
|
||||
|
||||
if (argv.length >= 2) {
|
||||
win.webContents.send('open-uri-requested', denormalizeUri(argv[1]));
|
||||
}
|
||||
} else if (argv.length >= 2) {
|
||||
openUri = denormalizeUri(argv[1]);
|
||||
}
|
||||
});
|
||||
|
||||
if (isSecondaryInstance) { // We're not in the original process, so quit
|
||||
quitNow();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
app.on('ready', function(){
|
||||
launchDaemonIfNotRunning();
|
||||
|
@ -324,6 +363,8 @@ function upgrade(event, installerPath) {
|
|||
|
||||
ipcMain.on('upgrade', upgrade);
|
||||
|
||||
app.setAsDefaultProtocolClient('lbry');
|
||||
|
||||
if (process.platform == 'darwin') {
|
||||
app.on('open-url', (event, uri) => {
|
||||
if (!win) {
|
||||
|
@ -333,7 +374,10 @@ if (process.platform == 'darwin') {
|
|||
win.webContents.send('open-uri-requested', uri);
|
||||
}
|
||||
});
|
||||
} else if (process.argv.length >= 3) {
|
||||
// No open-url event on Win, but we can still handle URIs provided at launch time
|
||||
win.webContents.send('open-uri-requested', process.argv[2]);
|
||||
} else if (process.argv.length >= 2) {
|
||||
if (!win) {
|
||||
openUri = denormalizeUri(process.argv[1]);
|
||||
} else {
|
||||
win.webContents.send('open-uri-requested', denormalizeUri(process.argv[1]));
|
||||
}
|
||||
}
|
||||
|
|
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,
|
||||
selectUpgradeFilename,
|
||||
selectPageTitle,
|
||||
selectCurrentPath,
|
||||
} from 'selectors/app'
|
||||
|
||||
const {remote, ipcRenderer, shell} = require('electron');
|
||||
|
@ -32,8 +31,7 @@ export function doNavigate(path, params = {}) {
|
|||
|
||||
const state = getState()
|
||||
const pageTitle = selectPageTitle(state)
|
||||
history.pushState(params, pageTitle, url)
|
||||
window.document.title = pageTitle
|
||||
dispatch(doHistoryPush(params, pageTitle, url))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +43,6 @@ export function doChangePath(path) {
|
|||
path,
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +52,15 @@ export function doHistoryBack() {
|
|||
}
|
||||
}
|
||||
|
||||
export function doLogoClick() {
|
||||
export function doHistoryPush(params, title, relativeUrl) {
|
||||
return function(dispatch, getState) {
|
||||
let pathParts = window.location.pathname.split('/')
|
||||
pathParts[pathParts.length - 1] = relativeUrl.replace(/^\//, '')
|
||||
const url = pathParts.join('/')
|
||||
title += " - LBRY"
|
||||
history.pushState(params, title, url)
|
||||
window.document.title = title
|
||||
}
|
||||
}
|
||||
|
||||
export function doOpenModal(modal) {
|
||||
|
|
|
@ -1,39 +1,29 @@
|
|||
import * as types from 'constants/action_types'
|
||||
import lbry from 'lbry'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
selectFetchingAvailability
|
||||
} from 'selectors/availability'
|
||||
|
||||
export function doFetchUriAvailability(uri) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.FETCH_AVAILABILITY_STARTED,
|
||||
data: { uri }
|
||||
})
|
||||
|
||||
const successCallback = (availability) => {
|
||||
dispatch({
|
||||
type: types.FETCH_AVAILABILITY_COMPLETED,
|
||||
data: {
|
||||
availability,
|
||||
uri,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const errorCallback = () => {
|
||||
console.debug('error')
|
||||
}
|
||||
|
||||
lbry.get_availability({ uri }, successCallback, errorCallback)
|
||||
}
|
||||
}
|
||||
|
||||
export function doFetchCurrentUriAvailability() {
|
||||
export function doFetchAvailability(uri) {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
const uri = selectCurrentUri(state)
|
||||
const alreadyFetching = !!selectFetchingAvailability(state)[uri]
|
||||
|
||||
dispatch(doFetchUriAvailability(uri))
|
||||
if (!alreadyFetching) {
|
||||
dispatch({
|
||||
type: types.FETCH_AVAILABILITY_STARTED,
|
||||
data: {uri}
|
||||
})
|
||||
|
||||
lbry.get_availability({uri}).then((availability) => {
|
||||
dispatch({
|
||||
type: types.FETCH_AVAILABILITY_COMPLETED,
|
||||
data: {
|
||||
availability,
|
||||
uri,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,18 +3,18 @@ import lbry from 'lbry'
|
|||
import lbryio from 'lbryio'
|
||||
import lbryuri from 'lbryuri'
|
||||
import rewards from 'rewards'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
import {
|
||||
selectBalance,
|
||||
} from 'selectors/wallet'
|
||||
import {
|
||||
selectCurrentUriFileInfo,
|
||||
selectDownloadingByUri,
|
||||
selectFileInfoForUri,
|
||||
selectUrisDownloading,
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
selectCurrentUriCostInfo,
|
||||
selectResolvingUris
|
||||
} from 'selectors/content'
|
||||
import {
|
||||
selectCostInfoForUri,
|
||||
} from 'selectors/cost_info'
|
||||
import {
|
||||
selectClaimsByUri,
|
||||
|
@ -22,94 +22,44 @@ import {
|
|||
import {
|
||||
doOpenModal,
|
||||
} from 'actions/app'
|
||||
import {
|
||||
doFetchCostInfoForUri,
|
||||
} from 'actions/cost_info'
|
||||
|
||||
export function doResolveUri(uri) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.RESOLVE_URI_STARTED,
|
||||
data: { uri }
|
||||
})
|
||||
|
||||
lbry.resolve({ uri }).then((resolutionInfo) => {
|
||||
const {
|
||||
claim,
|
||||
certificate,
|
||||
} = resolutionInfo ? resolutionInfo : { claim : null, certificate: null }
|
||||
const state = getState()
|
||||
const alreadyResolving = selectResolvingUris(state).indexOf(uri) !== -1
|
||||
|
||||
if (!alreadyResolving) {
|
||||
dispatch({
|
||||
type: types.RESOLVE_URI_COMPLETED,
|
||||
data: {
|
||||
uri,
|
||||
type: types.RESOLVE_URI_STARTED,
|
||||
data: { uri }
|
||||
})
|
||||
|
||||
lbry.resolve({ uri }).then((resolutionInfo) => {
|
||||
const {
|
||||
claim,
|
||||
certificate,
|
||||
}
|
||||
})
|
||||
|
||||
dispatch(doFetchCostInfoForUri(uri))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function doFetchDownloadedContent() {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_DOWNLOADED_CONTENT_STARTED,
|
||||
})
|
||||
|
||||
lbry.claim_list_mine().then((myClaimInfos) => {
|
||||
lbry.file_list().then((fileInfos) => {
|
||||
const myClaimOutpoints = myClaimInfos.map(({txid, nout}) => txid + ':' + nout);
|
||||
|
||||
fileInfos.forEach(fileInfo => {
|
||||
const uri = lbryuri.build({
|
||||
channelName: fileInfo.channel_name,
|
||||
contentName: fileInfo.name,
|
||||
})
|
||||
const claim = selectClaimsByUri(state)[uri]
|
||||
if (!claim) dispatch(doResolveUri(uri))
|
||||
})
|
||||
} = resolutionInfo ? resolutionInfo : { claim : null, certificate: null }
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_DOWNLOADED_CONTENT_COMPLETED,
|
||||
type: types.RESOLVE_URI_COMPLETED,
|
||||
data: {
|
||||
fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)),
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function doFetchPublishedContent() {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_PUBLISHED_CONTENT_STARTED,
|
||||
})
|
||||
|
||||
lbry.claim_list_mine().then((claimInfos) => {
|
||||
dispatch({
|
||||
type: types.FETCH_MY_CLAIMS_COMPLETED,
|
||||
data: {
|
||||
claims: claimInfos,
|
||||
}
|
||||
})
|
||||
lbry.file_list().then((fileInfos) => {
|
||||
const myClaimOutpoints = claimInfos.map(({txid, nout}) => txid + ':' + nout)
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_PUBLISHED_CONTENT_COMPLETED,
|
||||
data: {
|
||||
fileInfos: fileInfos.filter(({outpoint}) => myClaimOutpoints.includes(outpoint)),
|
||||
uri,
|
||||
claim,
|
||||
certificate,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function doCancelResolveUri(uri) {
|
||||
return function(dispatch, getState) {
|
||||
lbry.cancelResolve({ uri })
|
||||
dispatch({
|
||||
type: types.RESOLVE_URI_CANCELED,
|
||||
data: { uri }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +81,14 @@ export function doFetchFeaturedUris() {
|
|||
featuredUris[category] = Uris[category]
|
||||
}
|
||||
})
|
||||
//
|
||||
// dispatch({
|
||||
// type: types.FETCH_FEATURED_CONTENT_COMPLETED,
|
||||
// data: {
|
||||
// categories: ["FOO"],
|
||||
// uris: { FOO: ["lbry://gtasoc"]},
|
||||
// }
|
||||
// })
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_FEATURED_CONTENT_COMPLETED,
|
||||
|
@ -175,6 +133,7 @@ export function doUpdateLoadStatus(uri, outpoint) {
|
|||
type: types.DOWNLOADING_COMPLETED,
|
||||
data: {
|
||||
uri,
|
||||
outpoint,
|
||||
fileInfo,
|
||||
}
|
||||
})
|
||||
|
@ -190,6 +149,7 @@ export function doUpdateLoadStatus(uri, outpoint) {
|
|||
type: types.DOWNLOADING_PROGRESSED,
|
||||
data: {
|
||||
uri,
|
||||
outpoint,
|
||||
fileInfo,
|
||||
progress,
|
||||
}
|
||||
|
@ -200,13 +160,6 @@ export function doUpdateLoadStatus(uri, outpoint) {
|
|||
}
|
||||
}
|
||||
|
||||
export function doPlayVideo(uri) {
|
||||
return {
|
||||
type: types.PLAY_VIDEO_STARTED,
|
||||
data: { uri }
|
||||
}
|
||||
}
|
||||
|
||||
export function doDownloadFile(uri, streamInfo) {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
|
@ -216,6 +169,7 @@ export function doDownloadFile(uri, streamInfo) {
|
|||
type: types.DOWNLOADING_STARTED,
|
||||
data: {
|
||||
uri,
|
||||
outpoint: streamInfo.outpoint,
|
||||
fileInfo,
|
||||
}
|
||||
})
|
||||
|
@ -230,10 +184,9 @@ export function doDownloadFile(uri, streamInfo) {
|
|||
}
|
||||
}
|
||||
|
||||
export function doLoadVideo() {
|
||||
export function doLoadVideo(uri) {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
const uri = selectCurrentUri(state)
|
||||
|
||||
dispatch({
|
||||
type: types.LOADING_VIDEO_STARTED,
|
||||
|
@ -260,14 +213,13 @@ export function doLoadVideo() {
|
|||
}
|
||||
}
|
||||
|
||||
export function doWatchVideo() {
|
||||
export function doPurchaseUri(uri) {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
const uri = selectCurrentUri(state)
|
||||
const balance = selectBalance(state)
|
||||
const fileInfo = selectCurrentUriFileInfo(state)
|
||||
const costInfo = selectCurrentUriCostInfo(state)
|
||||
const downloadingByUri = selectDownloadingByUri(state)
|
||||
const fileInfo = selectFileInfoForUri(state, { uri })
|
||||
const costInfo = selectCostInfoForUri(state, { uri })
|
||||
const downloadingByUri = selectUrisDownloading(state)
|
||||
const alreadyDownloading = !!downloadingByUri[uri]
|
||||
const { cost } = costInfo
|
||||
|
||||
|
@ -288,8 +240,8 @@ export function doWatchVideo() {
|
|||
}
|
||||
|
||||
// the file is free or we have partially downloaded it
|
||||
if (cost <= 0.01 || fileInfo.download_directory) {
|
||||
dispatch(doLoadVideo())
|
||||
if (cost <= 0.01 || (fileInfo && fileInfo.download_directory)) {
|
||||
dispatch(doLoadVideo(uri))
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
|
@ -302,3 +254,44 @@ export function doWatchVideo() {
|
|||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export function doFetchClaimsByChannel(uri) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.FETCH_CHANNEL_CLAIMS_STARTED,
|
||||
data: { uri }
|
||||
})
|
||||
|
||||
lbry.resolve({ uri }).then((resolutionInfo) => {
|
||||
const {
|
||||
claims_in_channel,
|
||||
} = resolutionInfo ? resolutionInfo : { claims_in_channel: [] }
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_CHANNEL_CLAIMS_COMPLETED,
|
||||
data: {
|
||||
uri,
|
||||
claims: claims_in_channel
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function doClaimListMine() {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.CLAIM_LIST_MINE_STARTED
|
||||
})
|
||||
|
||||
|
||||
lbry.claim_list_mine().then((claims) => {
|
||||
dispatch({
|
||||
type: types.CLAIM_LIST_MINE_COMPLETED,
|
||||
data: {
|
||||
claims
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,19 +1,51 @@
|
|||
import * as types from 'constants/action_types'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
import lbry from 'lbry'
|
||||
import lbryio from 'lbryio'
|
||||
import {
|
||||
doResolveUri
|
||||
} from 'actions/content'
|
||||
import {
|
||||
selectResolvingUris,
|
||||
} from 'selectors/content'
|
||||
import {
|
||||
selectClaimsByUri
|
||||
} from 'selectors/claims'
|
||||
import {
|
||||
selectSettingsIsGenerous
|
||||
} from 'selectors/settings'
|
||||
|
||||
export function doFetchCostInfoForUri(uri) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.FETCH_COST_INFO_STARTED,
|
||||
data: {
|
||||
uri,
|
||||
}
|
||||
})
|
||||
const state = getState(),
|
||||
claim = selectClaimsByUri(state)[uri],
|
||||
isResolving = selectResolvingUris(state).indexOf(uri) !== -1,
|
||||
isGenerous = selectSettingsIsGenerous(state)
|
||||
|
||||
lbry.getCostInfo(uri).then(costInfo => {
|
||||
if (claim === null) { //claim doesn't exist, nothing to fetch a cost for
|
||||
return
|
||||
}
|
||||
|
||||
if (!claim) {
|
||||
setTimeout(() => {
|
||||
dispatch(doFetchCostInfoForUri(uri))
|
||||
}, 1000)
|
||||
if (!isResolving) {
|
||||
dispatch(doResolveUri(uri))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
function begin() {
|
||||
dispatch({
|
||||
type: types.FETCH_COST_INFO_STARTED,
|
||||
data: {
|
||||
uri,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function resolve(costInfo) {
|
||||
dispatch({
|
||||
type: types.FETCH_COST_INFO_COMPLETED,
|
||||
data: {
|
||||
|
@ -21,15 +53,25 @@ export function doFetchCostInfoForUri(uri) {
|
|||
costInfo,
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
dispatch({
|
||||
type: types.FETCH_COST_INFO_COMPLETED,
|
||||
data: {
|
||||
uri,
|
||||
costInfo: {}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (isGenerous && claim) {
|
||||
let cost
|
||||
const fee = claim.value.stream.metadata.fee;
|
||||
if (fee === undefined ) {
|
||||
resolve({ cost: 0, includesData: true })
|
||||
} else if (fee.currency == 'LBC') {
|
||||
resolve({ cost: fee.amount, includesData: true })
|
||||
} else {
|
||||
begin()
|
||||
lbryio.getExchangeRates().then(({lbc_usd}) => {
|
||||
resolve({ cost: fee.amount / lbc_usd, includesData: true })
|
||||
});
|
||||
}
|
||||
} else {
|
||||
begin()
|
||||
lbry.getCostInfo(uri).then(resolve)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import * as types from 'constants/action_types'
|
||||
import lbry from 'lbry'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
doClaimListMine
|
||||
} from 'actions/content'
|
||||
import {
|
||||
selectCurrentUriClaimOutpoint,
|
||||
selectClaimsByUri,
|
||||
selectClaimListMineIsPending,
|
||||
} from 'selectors/claims'
|
||||
import {
|
||||
selectFileListIsPending,
|
||||
selectAllFileInfos,
|
||||
selectUrisLoading,
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
doCloseModal,
|
||||
} from 'actions/app'
|
||||
|
@ -14,29 +20,54 @@ const {
|
|||
shell,
|
||||
} = require('electron')
|
||||
|
||||
export function doFetchCurrentUriFileInfo() {
|
||||
export function doFetchFileInfo(uri) {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
const uri = selectCurrentUri(state)
|
||||
const outpoint = selectCurrentUriClaimOutpoint(state)
|
||||
const claim = selectClaimsByUri(state)[uri]
|
||||
const outpoint = claim ? `${claim.txid}:${claim.nout}` : null
|
||||
const alreadyFetching = !!selectUrisLoading(state)[uri]
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_FILE_INFO_STARTED,
|
||||
data: {
|
||||
uri,
|
||||
outpoint,
|
||||
}
|
||||
})
|
||||
|
||||
lbry.file_list({ outpoint: outpoint, full_status: true }).then(([fileInfo]) => {
|
||||
if (!alreadyFetching) {
|
||||
dispatch({
|
||||
type: types.FETCH_FILE_INFO_COMPLETED,
|
||||
type: types.FETCH_FILE_INFO_STARTED,
|
||||
data: {
|
||||
uri,
|
||||
fileInfo,
|
||||
outpoint,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
lbry.file_list({outpoint: outpoint, full_status: true}).then(fileInfos => {
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_FILE_INFO_COMPLETED,
|
||||
data: {
|
||||
outpoint,
|
||||
fileInfo: fileInfos && fileInfos.length ? fileInfos[0] : null,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function doFileList() {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
const isPending = selectFileListIsPending(state)
|
||||
|
||||
if (!isPending) {
|
||||
dispatch({
|
||||
type: types.FILE_LIST_STARTED,
|
||||
})
|
||||
|
||||
lbry.file_list().then((fileInfos) => {
|
||||
dispatch({
|
||||
type: types.FILE_LIST_COMPLETED,
|
||||
data: {
|
||||
fileInfos,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,51 +83,39 @@ export function doOpenFileInFolder(fileInfo) {
|
|||
}
|
||||
}
|
||||
|
||||
export function doDeleteFile(uri, fileInfo, deleteFromComputer) {
|
||||
export function doDeleteFile(outpoint, deleteFromComputer) {
|
||||
return function(dispatch, getState) {
|
||||
|
||||
dispatch({
|
||||
type: types.DELETE_FILE_STARTED,
|
||||
type: types.FILE_DELETE,
|
||||
data: {
|
||||
uri,
|
||||
fileInfo,
|
||||
deleteFromComputer,
|
||||
outpoint
|
||||
}
|
||||
})
|
||||
|
||||
const successCallback = () => {
|
||||
dispatch({
|
||||
type: types.DELETE_FILE_COMPLETED,
|
||||
data: {
|
||||
uri,
|
||||
}
|
||||
})
|
||||
dispatch(doCloseModal())
|
||||
}
|
||||
|
||||
lbry.removeFile(fileInfo.outpoint, deleteFromComputer, successCallback)
|
||||
}
|
||||
}
|
||||
|
||||
export function doFetchDownloadedContent() {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_DOWNLOADED_CONTENT_STARTED,
|
||||
lbry.file_delete({
|
||||
outpoint: outpoint,
|
||||
delete_target_file: deleteFromComputer,
|
||||
})
|
||||
|
||||
lbry.claim_list_mine().then((myClaimInfos) => {
|
||||
lbry.file_list().then((fileInfos) => {
|
||||
const myClaimOutpoints = myClaimInfos.map(({txid, nout}) => txid + ':' + nout);
|
||||
|
||||
dispatch({
|
||||
type: types.FETCH_DOWNLOADED_CONTENT_COMPLETED,
|
||||
data: {
|
||||
fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)),
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
dispatch(doCloseModal())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function doFetchFileInfosAndPublishedClaims() {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState(),
|
||||
isClaimListMinePending = selectClaimListMineIsPending(state),
|
||||
isFileInfoListPending = selectFileListIsPending(state)
|
||||
|
||||
if (isClaimListMinePending === undefined) {
|
||||
dispatch(doClaimListMine())
|
||||
}
|
||||
|
||||
if (isFileInfoListPending === undefined) {
|
||||
dispatch(doFileList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
} from 'actions/content'
|
||||
import {
|
||||
doNavigate,
|
||||
doHistoryPush
|
||||
} from 'actions/app'
|
||||
import {
|
||||
selectCurrentPage,
|
||||
|
@ -29,6 +30,8 @@ export function doSearch(query) {
|
|||
|
||||
if(page != 'search') {
|
||||
dispatch(doNavigate('search', { query: query }))
|
||||
} else {
|
||||
dispatch(doHistoryPush({ query }, "Search for " + query, '/search'))
|
||||
}
|
||||
|
||||
lighthouse.search(query).then(results => {
|
||||
|
|
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 {
|
||||
selectCurrentPage,
|
||||
selectCurrentModal,
|
||||
selectDrawerOpen,
|
||||
selectHeaderLinks,
|
||||
selectSearchTerm,
|
||||
} from 'selectors/app'
|
||||
import {
|
||||
doCheckUpgradeAvailable,
|
||||
doOpenModal,
|
||||
doCloseModal,
|
||||
} from 'actions/app'
|
||||
import {
|
||||
doUpdateBalance,
|
||||
} from 'actions/wallet'
|
||||
import App from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
currentPage: selectCurrentPage(state),
|
||||
modal: selectCurrentModal(state),
|
||||
headerLinks: selectHeaderLinks(state),
|
||||
searchTerm: selectSearchTerm(state)
|
||||
})
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
||||
openModal: () => dispatch(doOpenModal()),
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
updateBalance: (balance) => dispatch(doUpdateBalance(balance))
|
||||
})
|
||||
|
||||
export default connect(select, perform)(App)
|
||||
|
|
|
@ -4,7 +4,8 @@ import Header from 'component/header';
|
|||
import ErrorModal from 'component/errorModal'
|
||||
import DownloadingModal from 'component/downloadingModal'
|
||||
import UpgradeModal from 'component/upgradeModal'
|
||||
import {Line} from 'rc-progress';
|
||||
import lbry from 'lbry'
|
||||
import {Line} from 'rc-progress'
|
||||
|
||||
class App extends React.Component {
|
||||
componentWillMount() {
|
||||
|
@ -15,12 +16,15 @@ class App extends React.Component {
|
|||
if (!this.props.upgradeSkipped) {
|
||||
this.props.checkUpgradeAvailable()
|
||||
}
|
||||
|
||||
lbry.balanceSubscribe((balance) => {
|
||||
this.props.updateBalance(balance)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
modal,
|
||||
headerLinks,
|
||||
} = this.props
|
||||
|
||||
return <div id="window">
|
||||
|
|
|
@ -200,7 +200,7 @@ const CodeRequiredStage = React.createClass({
|
|||
})
|
||||
|
||||
if (!this.state.address) {
|
||||
lbry.getUnusedAddress((address) => {
|
||||
lbry.wallet_unused_address().then((address) => {
|
||||
setLocal('wallet_address', address);
|
||||
this.setState({ address: address });
|
||||
});
|
||||
|
|
|
@ -3,9 +3,6 @@ import {
|
|||
connect,
|
||||
} from 'react-redux'
|
||||
import {
|
||||
selectObscureNsfw,
|
||||
selectHidePrice,
|
||||
selectHasSignature,
|
||||
selectPlatform,
|
||||
} from 'selectors/app'
|
||||
import {
|
||||
|
@ -14,7 +11,7 @@ import {
|
|||
makeSelectLoadingForUri,
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
makeSelectAvailabilityForUri,
|
||||
makeSelectIsAvailableForUri,
|
||||
} from 'selectors/availability'
|
||||
import {
|
||||
selectCurrentModal,
|
||||
|
@ -22,47 +19,50 @@ import {
|
|||
import {
|
||||
doCloseModal,
|
||||
doOpenModal,
|
||||
doHistoryBack,
|
||||
} from 'actions/app'
|
||||
import {
|
||||
doFetchAvailability
|
||||
} from 'actions/availability'
|
||||
import {
|
||||
doOpenFileInShell,
|
||||
doOpenFileInFolder,
|
||||
doDeleteFile,
|
||||
} from 'actions/file_info'
|
||||
import {
|
||||
doWatchVideo,
|
||||
doPurchaseUri,
|
||||
doLoadVideo,
|
||||
} from 'actions/content'
|
||||
import FileActions from './view'
|
||||
|
||||
const makeSelect = () => {
|
||||
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
||||
const selectAvailabilityForUri = makeSelectAvailabilityForUri()
|
||||
const selectIsAvailableForUri = makeSelectIsAvailableForUri()
|
||||
const selectDownloadingForUri = makeSelectDownloadingForUri()
|
||||
const selectLoadingForUri = makeSelectLoadingForUri()
|
||||
|
||||
const select = (state, props) => ({
|
||||
obscureNsfw: selectObscureNsfw(state),
|
||||
hidePrice: selectHidePrice(state),
|
||||
hasSignature: selectHasSignature(state),
|
||||
fileInfo: selectFileInfoForUri(state, props),
|
||||
availability: selectAvailabilityForUri(state, props),
|
||||
isAvailable: selectIsAvailableForUri(state, props),
|
||||
platform: selectPlatform(state),
|
||||
modal: selectCurrentModal(state),
|
||||
downloading: selectDownloadingForUri(state, props),
|
||||
loading: selectLoadingForUri(state, props),
|
||||
})
|
||||
|
||||
return select
|
||||
}
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
checkAvailability: (uri) => dispatch(doFetchAvailability(uri)),
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
openInFolder: (fileInfo) => dispatch(doOpenFileInFolder(fileInfo)),
|
||||
openInShell: (fileInfo) => dispatch(doOpenFileInShell(fileInfo)),
|
||||
deleteFile: (fileInfo, deleteFromComputer) => dispatch(doDeleteFile(fileInfo, deleteFromComputer)),
|
||||
deleteFile: (fileInfo, deleteFromComputer) => {
|
||||
dispatch(doHistoryBack())
|
||||
dispatch(doDeleteFile(fileInfo, deleteFromComputer))
|
||||
},
|
||||
openModal: (modal) => dispatch(doOpenModal(modal)),
|
||||
downloadClick: () => dispatch(doWatchVideo()),
|
||||
loadVideo: () => dispatch(doLoadVideo())
|
||||
startDownload: (uri) => dispatch(doPurchaseUri(uri)),
|
||||
loadVideo: (uri) => dispatch(doLoadVideo(uri))
|
||||
})
|
||||
|
||||
export default connect(makeSelect, perform)(FileActions)
|
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import lbryuri from 'lbryuri';
|
||||
import {Icon,} from 'component/common';
|
||||
import {Icon,BusyMessage} from 'component/common';
|
||||
import FilePrice from 'component/filePrice'
|
||||
import {Modal} from 'component/modal';
|
||||
import {FormField} from 'component/form';
|
||||
|
@ -9,14 +7,36 @@ import Link from 'component/link';
|
|||
import {ToolTip} from 'component/tooltip';
|
||||
import {DropDownMenu, DropDownMenuItem} from 'component/menu';
|
||||
|
||||
class FileActionsRow extends React.Component {
|
||||
class FileActions extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
forceShowActions: false,
|
||||
deleteChecked: false,
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.checkAvailability(this.props.uri)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.checkAvailability(nextProps.uri)
|
||||
}
|
||||
|
||||
checkAvailability(uri) {
|
||||
if (!this._uri || uri !== this._uri) {
|
||||
this._uri = uri;
|
||||
this.props.checkAvailability(uri)
|
||||
}
|
||||
}
|
||||
|
||||
onShowFileActionsRowClicked() {
|
||||
this.setState({
|
||||
forceShowActions: true,
|
||||
});
|
||||
}
|
||||
|
||||
handleDeleteCheckboxClicked(event) {
|
||||
this.setState({
|
||||
deleteChecked: event.target.checked,
|
||||
|
@ -25,65 +45,73 @@ class FileActionsRow extends React.Component {
|
|||
|
||||
onAffirmPurchase() {
|
||||
this.props.closeModal()
|
||||
this.props.loadVideo()
|
||||
this.props.loadVideo(this.props.uri)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
fileInfo,
|
||||
isAvailable,
|
||||
platform,
|
||||
downloading,
|
||||
loading,
|
||||
uri,
|
||||
deleteFile,
|
||||
openInFolder,
|
||||
openInShell,
|
||||
modal,
|
||||
openModal,
|
||||
affirmPurchase,
|
||||
closeModal,
|
||||
downloadClick,
|
||||
startDownload,
|
||||
} = this.props
|
||||
|
||||
const {
|
||||
deleteChecked,
|
||||
} = this.state
|
||||
const deleteChecked = this.state.deleteChecked,
|
||||
metadata = fileInfo ? fileInfo.metadata : null,
|
||||
openInFolderMessage = platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder',
|
||||
showMenu = fileInfo && Object.keys(fileInfo).length > 0,
|
||||
title = metadata ? metadata.title : uri;
|
||||
|
||||
const metadata = fileInfo ? fileInfo.metadata : null
|
||||
let content
|
||||
|
||||
if (!fileInfo)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (downloading) {
|
||||
|
||||
const openInFolderMessage = platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder',
|
||||
showMenu = Object.keys(fileInfo).length != 0;
|
||||
|
||||
let linkBlock;
|
||||
if (Object.keys(fileInfo).length == 0 && !downloading && !loading) {
|
||||
linkBlock = <Link button="text" label="Download" icon="icon-download" onClick={downloadClick} />;
|
||||
} else if (downloading || loading) {
|
||||
const
|
||||
progress = (fileInfo && fileInfo.written_bytes) ? fileInfo.written_bytes / fileInfo.total_bytes * 100 : 0,
|
||||
label = fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...',
|
||||
labelWithIcon = <span className="button__content"><Icon icon="icon-download" /><span>{label}</span></span>;
|
||||
|
||||
linkBlock = (
|
||||
<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>
|
||||
{labelWithIcon}
|
||||
</div>
|
||||
);
|
||||
content = <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>
|
||||
{labelWithIcon}
|
||||
</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 {
|
||||
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 (
|
||||
<div>
|
||||
{fileInfo !== null || fileInfo.isMine
|
||||
? linkBlock
|
||||
: null}
|
||||
<section className="file-actions">
|
||||
{ content }
|
||||
{ showMenu ?
|
||||
<DropDownMenu>
|
||||
<DropDownMenuItem key={0} onClick={() => openInFolder(fileInfo)} label={openInFolderMessage} />
|
||||
|
@ -91,7 +119,7 @@ class FileActionsRow extends React.Component {
|
|||
</DropDownMenu> : '' }
|
||||
<Modal type="confirm" isOpen={modal == 'affirmPurchase'}
|
||||
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 isOpen={modal == 'notEnoughCredits'} contentLabel="Not enough credits"
|
||||
onConfirmed={closeModal}>
|
||||
|
@ -102,62 +130,18 @@ class FileActionsRow extends React.Component {
|
|||
LBRY was unable to download the stream <strong>{uri}</strong>.
|
||||
</Modal>
|
||||
<Modal isOpen={modal == 'confirmRemove'}
|
||||
contentLabel="Not enough credits"
|
||||
type="confirm"
|
||||
confirmButtonLabel="Remove"
|
||||
onConfirmed={() => deleteFile(uri, fileInfo, deleteChecked)}
|
||||
onAborted={closeModal}>
|
||||
contentLabel="Not enough credits"
|
||||
type="confirm"
|
||||
confirmButtonLabel="Remove"
|
||||
onConfirmed={() => deleteFile(fileInfo.outpoint, deleteChecked)}
|
||||
onAborted={closeModal}>
|
||||
<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>
|
||||
</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
|
||||
|
|
|
@ -7,21 +7,20 @@ import {
|
|||
} from 'actions/app'
|
||||
import {
|
||||
doResolveUri,
|
||||
doCancelResolveUri,
|
||||
} from 'actions/content'
|
||||
import {
|
||||
selectHidePrice,
|
||||
selectObscureNsfw,
|
||||
} from 'selectors/app'
|
||||
import {
|
||||
makeSelectClaimForUri,
|
||||
makeSelectSourceForUri,
|
||||
makeSelectMetadataForUri,
|
||||
} from 'selectors/claims'
|
||||
import {
|
||||
makeSelectFileInfoForUri,
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
makeSelectResolvingUri,
|
||||
makeSelectIsResolvingForUri,
|
||||
} from 'selectors/content'
|
||||
import FileCard from './view'
|
||||
|
||||
|
@ -29,17 +28,13 @@ const makeSelect = () => {
|
|||
const selectClaimForUri = makeSelectClaimForUri()
|
||||
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
||||
const selectMetadataForUri = makeSelectMetadataForUri()
|
||||
const selectSourceForUri = makeSelectSourceForUri()
|
||||
const selectResolvingUri = makeSelectResolvingUri()
|
||||
const selectResolvingUri = makeSelectIsResolvingForUri()
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: selectClaimForUri(state, props),
|
||||
fileInfo: selectFileInfoForUri(state, props),
|
||||
hidePrice: selectHidePrice(state),
|
||||
obscureNsfw: selectObscureNsfw(state),
|
||||
hasSignature: false,
|
||||
metadata: selectMetadataForUri(state, props),
|
||||
source: selectSourceForUri(state, props),
|
||||
isResolvingUri: selectResolvingUri(state, props),
|
||||
})
|
||||
|
||||
|
@ -49,6 +44,7 @@ const makeSelect = () => {
|
|||
const perform = (dispatch) => ({
|
||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||
resolveUri: (uri) => dispatch(doResolveUri(uri)),
|
||||
cancelResolveUri: (uri) => dispatch(doCancelResolveUri(uri))
|
||||
})
|
||||
|
||||
export default connect(makeSelect, perform)(FileCard)
|
||||
|
|
|
@ -2,21 +2,41 @@ import React from 'react';
|
|||
import lbry from 'lbry.js';
|
||||
import lbryuri from 'lbryuri.js';
|
||||
import Link from 'component/link';
|
||||
import {Thumbnail, TruncatedText,} from 'component/common';
|
||||
import {Thumbnail, TruncatedText, Icon} from 'component/common';
|
||||
import FilePrice from 'component/filePrice'
|
||||
import UriIndicator from 'component/uriIndicator';
|
||||
|
||||
class FileCard extends React.Component {
|
||||
componentDidMount() {
|
||||
componentWillMount() {
|
||||
this.resolve(this.props)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.resolve(nextProps)
|
||||
}
|
||||
|
||||
resolve(props) {
|
||||
const {
|
||||
isResolvingUri,
|
||||
resolveUri,
|
||||
claim,
|
||||
uri,
|
||||
} = props
|
||||
|
||||
if(!isResolvingUri && claim === undefined && uri) {
|
||||
resolveUri(uri)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const {
|
||||
isResolvingUri,
|
||||
cancelResolveUri,
|
||||
uri
|
||||
} = this.props
|
||||
|
||||
if(!isResolvingUri && !claim && uri) {
|
||||
resolveUri(uri)
|
||||
if (isResolvingUri) {
|
||||
cancelResolveUri(uri)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,10 +55,11 @@ class FileCard extends React.Component {
|
|||
render() {
|
||||
|
||||
const {
|
||||
claim,
|
||||
fileInfo,
|
||||
metadata,
|
||||
isResolvingUri,
|
||||
navigate,
|
||||
hidePrice,
|
||||
} = this.props
|
||||
|
||||
const uri = lbryuri.normalize(this.props.uri);
|
||||
|
@ -50,6 +71,8 @@ class FileCard extends React.Component {
|
|||
description = "Loading..."
|
||||
} else if (metadata && metadata.description) {
|
||||
description = metadata.description
|
||||
} else if (claim === null) {
|
||||
description = 'This address contains no content.'
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -59,7 +82,10 @@ class FileCard extends React.Component {
|
|||
<div className="card__title-identity">
|
||||
<h5 title={title}><TruncatedText lines={1}>{title}</TruncatedText></h5>
|
||||
<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} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -58,8 +58,8 @@ class FileList extends React.Component {
|
|||
render() {
|
||||
const {
|
||||
handleSortChanged,
|
||||
fetching,
|
||||
fileInfos,
|
||||
hidePrices,
|
||||
} = this.props
|
||||
const {
|
||||
sortBy,
|
||||
|
@ -71,10 +71,11 @@ class FileList extends React.Component {
|
|||
contentName: fileInfo.name,
|
||||
channelName: fileInfo.channel_name,
|
||||
})
|
||||
content.push(<FileTile key={uri} uri={uri} hidePrice={hidePrices} hideOnRemove={true} showEmpty={""} />)
|
||||
content.push(<FileTile key={uri} uri={uri} hidePrice={true} showEmpty={this.props.fileTileShowEmpty} />)
|
||||
})
|
||||
return (
|
||||
<section>
|
||||
<section className="file-list__header">
|
||||
{ fetching && <span className="busy-indicator"/> }
|
||||
<span className='sort-section'>
|
||||
Sort by { ' ' }
|
||||
<FormField type="select" onChange={this.handleSortChanged.bind(this)}>
|
||||
|
|
|
@ -2,6 +2,9 @@ import React from 'react'
|
|||
import {
|
||||
connect,
|
||||
} from 'react-redux'
|
||||
import {
|
||||
doFetchCostInfoForUri,
|
||||
} from 'actions/cost_info'
|
||||
import {
|
||||
makeSelectCostInfoForUri,
|
||||
} from 'selectors/cost_info'
|
||||
|
@ -9,6 +12,7 @@ import FilePrice from './view'
|
|||
|
||||
const makeSelect = () => {
|
||||
const selectCostInfoForUri = makeSelectCostInfoForUri()
|
||||
|
||||
const select = (state, props) => ({
|
||||
costInfo: selectCostInfoForUri(state, props),
|
||||
})
|
||||
|
@ -17,6 +21,8 @@ const makeSelect = () => {
|
|||
}
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
fetchCostInfo: (uri) => dispatch(doFetchCostInfoForUri(uri)),
|
||||
// cancelFetchCostInfo: (uri) => dispatch(doCancelFetchCostInfoForUri(uri))
|
||||
})
|
||||
|
||||
export default connect(makeSelect, perform)(FilePrice)
|
||||
|
|
|
@ -3,19 +3,41 @@ import {
|
|||
CreditAmount,
|
||||
} from 'component/common'
|
||||
|
||||
const FilePrice = (props) => {
|
||||
const {
|
||||
costInfo,
|
||||
look = 'indicator',
|
||||
} = props
|
||||
|
||||
const isEstimate = costInfo ? !costInfo.includesData : null
|
||||
|
||||
if (!costInfo) {
|
||||
return <span className={`credit-amount credit-amount--${look}`}>???</span>;
|
||||
class FilePrice extends React.Component{
|
||||
componentWillMount() {
|
||||
this.fetchCost(this.props)
|
||||
}
|
||||
|
||||
return <CreditAmount label={false} amount={costInfo.cost} isEstimate={isEstimate} showFree={true} />
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.fetchCost(nextProps)
|
||||
}
|
||||
|
||||
fetchCost(props) {
|
||||
const {
|
||||
costInfo,
|
||||
fetchCostInfo,
|
||||
uri
|
||||
} = props
|
||||
|
||||
if (costInfo === undefined) {
|
||||
fetchCostInfo(uri)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
costInfo,
|
||||
look = 'indicator',
|
||||
} = this.props
|
||||
|
||||
const isEstimate = costInfo ? !costInfo.includesData : null
|
||||
|
||||
if (!costInfo) {
|
||||
return <span className={`credit-amount credit-amount--${look}`}>???</span>;
|
||||
}
|
||||
|
||||
return <CreditAmount label={false} amount={costInfo.cost} isEstimate={isEstimate} showFree={true} />
|
||||
}
|
||||
}
|
||||
|
||||
export default FilePrice
|
||||
|
|
|
@ -10,41 +10,30 @@ import {
|
|||
} from 'actions/content'
|
||||
import {
|
||||
makeSelectClaimForUri,
|
||||
makeSelectSourceForUri,
|
||||
makeSelectMetadataForUri,
|
||||
} from 'selectors/claims'
|
||||
import {
|
||||
makeSelectFileInfoForUri,
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
makeSelectFetchingAvailabilityForUri,
|
||||
makeSelectAvailabilityForUri,
|
||||
} from 'selectors/availability'
|
||||
import {
|
||||
selectObscureNsfw,
|
||||
} from 'selectors/app'
|
||||
import {
|
||||
makeSelectResolvingUri,
|
||||
makeSelectIsResolvingForUri,
|
||||
} from 'selectors/content'
|
||||
import FileTile from './view'
|
||||
|
||||
const makeSelect = () => {
|
||||
const selectClaimForUri = makeSelectClaimForUri()
|
||||
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
||||
const selectFetchingAvailabilityForUri = makeSelectFetchingAvailabilityForUri()
|
||||
const selectAvailabilityForUri = makeSelectAvailabilityForUri()
|
||||
const selectMetadataForUri = makeSelectMetadataForUri()
|
||||
const selectSourceForUri = makeSelectSourceForUri()
|
||||
const selectResolvingUri = makeSelectResolvingUri()
|
||||
const selectResolvingUri = makeSelectIsResolvingForUri()
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: selectClaimForUri(state, props),
|
||||
fileInfo: selectFileInfoForUri(state, props),
|
||||
fetchingAvailability: selectFetchingAvailabilityForUri(state, props),
|
||||
selectAvailabilityForUri: selectAvailabilityForUri(state, props),
|
||||
obscureNsfw: selectObscureNsfw(state),
|
||||
metadata: selectMetadataForUri(state, props),
|
||||
source: selectSourceForUri(state, props),
|
||||
isResolvingUri: selectResolvingUri(state, props),
|
||||
})
|
||||
|
||||
|
|
|
@ -13,11 +13,8 @@ class FileTile extends React.Component {
|
|||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this._fileInfoSubscribeId = null
|
||||
this._isMounted = null
|
||||
this.state = {
|
||||
showNsfwHelp: false,
|
||||
isHidden: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,31 +26,11 @@ class FileTile extends React.Component {
|
|||
uri,
|
||||
} = this.props
|
||||
|
||||
this._isMounted = true;
|
||||
|
||||
if (this.props.hideOnRemove) {
|
||||
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
|
||||
}
|
||||
|
||||
if(!isResolvingUri && !claim && uri) {
|
||||
resolveUri(uri)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._fileInfoSubscribeId) {
|
||||
lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId);
|
||||
}
|
||||
}
|
||||
|
||||
onFileInfoUpdate(fileInfo) {
|
||||
if (!fileInfo && this._isMounted && this.props.hideOnRemove) {
|
||||
this.setState({
|
||||
isHidden: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseOver() {
|
||||
if (this.props.obscureNsfw && this.props.metadata && this.props.metadata.nsfw) {
|
||||
this.setState({
|
||||
|
@ -71,10 +48,6 @@ class FileTile extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (this.state.isHidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
claim,
|
||||
metadata,
|
||||
|
@ -86,18 +59,22 @@ class FileTile extends React.Component {
|
|||
|
||||
const uri = lbryuri.normalize(this.props.uri);
|
||||
const isClaimed = !!claim;
|
||||
const isClaimable = lbryuri.isClaimable(uri)
|
||||
const title = isClaimed && metadata && metadata.title ? metadata.title : uri;
|
||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||
let onClick = () => navigate('/show', { uri })
|
||||
|
||||
let description = ""
|
||||
if (isClaimed) {
|
||||
description = metadata.description
|
||||
description = metadata && metadata.description
|
||||
} else if (isResolvingUri) {
|
||||
description = "Loading..."
|
||||
} else if (showEmpty === FileTile.SHOW_EMPTY_PUBLISH) {
|
||||
onClick = () => navigate('/publish')
|
||||
description = <span className="empty">This location is unclaimed - <span className="button-text">put something here</span>!</span>
|
||||
onClick = () => navigate('/publish', { })
|
||||
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) {
|
||||
description = <span className="empty">This file is pending confirmation.</span>
|
||||
}
|
||||
|
|
|
@ -2,14 +2,13 @@ import React from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import Router from './view.jsx';
|
||||
import {
|
||||
selectCurrentPage
|
||||
selectCurrentPage,
|
||||
selectCurrentParams,
|
||||
} from 'selectors/app.js';
|
||||
|
||||
const select = (state) => ({
|
||||
params: selectCurrentParams(state),
|
||||
currentPage: selectCurrentPage(state)
|
||||
})
|
||||
|
||||
const perform = {
|
||||
}
|
||||
|
||||
export default connect(select, null)(Router);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import SettingsPage from 'page/settings.js';
|
||||
import SettingsPage from 'page/settings';
|
||||
import HelpPage from 'page/help';
|
||||
import ReportPage from 'page/report.js';
|
||||
import StartPage from 'page/start.js';
|
||||
|
@ -25,25 +25,26 @@ const route = (page, routesMap) => {
|
|||
const Router = (props) => {
|
||||
const {
|
||||
currentPage,
|
||||
params,
|
||||
} = props;
|
||||
|
||||
return route(currentPage, {
|
||||
'settings': <SettingsPage {...props} />,
|
||||
'help': <HelpPage {...props} />,
|
||||
'report': <ReportPage {...props} />,
|
||||
'downloaded': <FileListDownloaded {...props} />,
|
||||
'published': <FileListPublished {...props} />,
|
||||
'start': <StartPage {...props} />,
|
||||
'wallet': <WalletPage {...props} />,
|
||||
'send': <WalletPage {...props} />,
|
||||
'receive': <WalletPage {...props} />,
|
||||
'show': <ShowPage {...props} />,
|
||||
'channel': <ChannelPage {...props} />,
|
||||
'publish': <PublishPage {...props} />,
|
||||
'developer': <DeveloperPage {...props} />,
|
||||
'discover': <DiscoverPage {...props} />,
|
||||
'rewards': <RewardsPage {...props} />,
|
||||
'search': <SearchPage {...props} />,
|
||||
'settings': <SettingsPage {...params} />,
|
||||
'help': <HelpPage {...params} />,
|
||||
'report': <ReportPage {...params} />,
|
||||
'downloaded': <FileListDownloaded {...params} />,
|
||||
'published': <FileListPublished {...params} />,
|
||||
'start': <StartPage {...params} />,
|
||||
'wallet': <WalletPage {...params} />,
|
||||
'send': <WalletPage {...params} />,
|
||||
'receive': <WalletPage {...params} />,
|
||||
'show': <ShowPage {...params} />,
|
||||
'channel': <ChannelPage {...params} />,
|
||||
'publish': <PublishPage {...params} />,
|
||||
'developer': <DeveloperPage {...params} />,
|
||||
'discover': <DiscoverPage {...params} />,
|
||||
'rewards': <RewardsPage {...params} />,
|
||||
'search': <SearchPage {...params} />,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -9,34 +9,46 @@ import {
|
|||
selectCurrentModal,
|
||||
} from 'selectors/app'
|
||||
import {
|
||||
doWatchVideo,
|
||||
doPurchaseUri,
|
||||
doLoadVideo,
|
||||
} from 'actions/content'
|
||||
import {
|
||||
selectLoadingCurrentUri,
|
||||
selectCurrentUriFileReadyToPlay,
|
||||
selectCurrentUriIsPlaying,
|
||||
selectCurrentUriFileInfo,
|
||||
selectDownloadingCurrentUri,
|
||||
makeSelectMetadataForUri
|
||||
} from 'selectors/claims'
|
||||
import {
|
||||
makeSelectFileInfoForUri,
|
||||
makeSelectLoadingForUri,
|
||||
makeSelectDownloadingForUri,
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
selectCurrentUriCostInfo,
|
||||
makeSelectCostInfoForUri,
|
||||
} from 'selectors/cost_info'
|
||||
import Video from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
costInfo: selectCurrentUriCostInfo(state),
|
||||
fileInfo: selectCurrentUriFileInfo(state),
|
||||
modal: selectCurrentModal(state),
|
||||
isLoading: selectLoadingCurrentUri(state),
|
||||
readyToPlay: selectCurrentUriFileReadyToPlay(state),
|
||||
isDownloading: selectDownloadingCurrentUri(state),
|
||||
})
|
||||
|
||||
const makeSelect = () => {
|
||||
const selectCostInfo = makeSelectCostInfoForUri()
|
||||
const selectFileInfo = makeSelectFileInfoForUri()
|
||||
const selectIsLoading = makeSelectLoadingForUri()
|
||||
const selectIsDownloading = makeSelectDownloadingForUri()
|
||||
const selectMetadata = makeSelectMetadataForUri()
|
||||
|
||||
const select = (state, props) => ({
|
||||
costInfo: selectCostInfo(state, props),
|
||||
fileInfo: selectFileInfo(state, props),
|
||||
metadata: selectMetadata(state, props),
|
||||
modal: selectCurrentModal(state),
|
||||
isLoading: selectIsLoading(state, props),
|
||||
isDownloading: selectIsDownloading(state, props),
|
||||
})
|
||||
|
||||
return select
|
||||
}
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
loadVideo: () => dispatch(doLoadVideo()),
|
||||
watchVideo: (elem) => dispatch(doWatchVideo()),
|
||||
loadVideo: (uri) => dispatch(doLoadVideo(uri)),
|
||||
purchaseUri: (uri) => dispatch(doPurchaseUri(uri)),
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
})
|
||||
|
||||
export default connect(select, perform)(Video)
|
||||
export default connect(makeSelect, perform)(Video)
|
|
@ -1,17 +1,21 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Icon,
|
||||
Thumbnail,
|
||||
} from 'component/common';
|
||||
import FilePrice from 'component/filePrice'
|
||||
import Link from 'component/link';
|
||||
import Modal from 'component/modal';
|
||||
|
||||
class WatchLink extends React.Component {
|
||||
confirmPurchaseClick() {
|
||||
class VideoPlayButton extends React.Component {
|
||||
onPurchaseConfirmed() {
|
||||
this.props.closeModal()
|
||||
this.props.startPlaying()
|
||||
this.props.loadVideo()
|
||||
this.props.loadVideo(this.props.uri)
|
||||
}
|
||||
|
||||
onWatchClick() {
|
||||
this.props.purchaseUri(this.props.uri).then(() => {
|
||||
if (!this.props.modal) {
|
||||
this.props.startPlaying()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -19,7 +23,6 @@ class WatchLink extends React.Component {
|
|||
button,
|
||||
label,
|
||||
className,
|
||||
onWatchClick,
|
||||
metadata,
|
||||
metadata: {
|
||||
title,
|
||||
|
@ -32,13 +35,21 @@ class WatchLink extends React.Component {
|
|||
fileInfo,
|
||||
} = this.props
|
||||
|
||||
/*
|
||||
title={
|
||||
isLoading ? "Video is Loading" :
|
||||
!costInfo ? "Waiting on cost info..." :
|
||||
fileInfo === undefined ? "Waiting on file info..." : ""
|
||||
}
|
||||
*/
|
||||
|
||||
return (<div>
|
||||
<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 : ""}
|
||||
className="video__play-button"
|
||||
icon="icon-play"
|
||||
onClick={onWatchClick} />
|
||||
onClick={this.onWatchClick.bind(this)} />
|
||||
{modal}
|
||||
<Modal contentLabel="Not enough credits" isOpen={modal == 'notEnoughCredits'} onConfirmed={closeModal}>
|
||||
You don't have enough LBRY credits to pay for this stream.
|
||||
|
@ -47,9 +58,9 @@ class WatchLink extends React.Component {
|
|||
type="confirm"
|
||||
isOpen={modal == 'affirmPurchase'}
|
||||
contentLabel="Confirm Purchase"
|
||||
onConfirmed={this.confirmPurchaseClick.bind(this)}
|
||||
onConfirmed={this.onPurchaseConfirmed.bind(this)}
|
||||
onAborted={closeModal}>
|
||||
Are you sure you'd like to buy <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
|
||||
isOpen={modal == 'timedOut'} onConfirmed={closeModal} contentLabel="Timed Out">
|
||||
|
@ -67,16 +78,6 @@ class Video extends React.Component {
|
|||
this.state = { isPlaying: false }
|
||||
}
|
||||
|
||||
onWatchClick() {
|
||||
this.props.watchVideo().then(() => {
|
||||
if (!this.props.modal) {
|
||||
this.setState({
|
||||
isPlaying: true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
startPlaying() {
|
||||
this.setState({
|
||||
isPlaying: true
|
||||
|
@ -85,8 +86,6 @@ class Video extends React.Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
readyToPlay = false,
|
||||
thumbnail,
|
||||
metadata,
|
||||
isLoading,
|
||||
isDownloading,
|
||||
|
@ -96,6 +95,8 @@ class Video extends React.Component {
|
|||
isPlaying = false,
|
||||
} = this.state
|
||||
|
||||
const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0
|
||||
|
||||
let loadStatusMessage = ''
|
||||
|
||||
if (isLoading) {
|
||||
|
@ -105,39 +106,42 @@ class Video extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={"video " + this.props.className + (isPlaying && readyToPlay ? " video--active" : " video--hidden")}>{
|
||||
isPlaying ?
|
||||
!readyToPlay ?
|
||||
<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} /> :
|
||||
<div className={"video " + this.props.className + (isPlaying ? " video--active" : " video--hidden")}>{
|
||||
isPlaying || isLoading ?
|
||||
(!isReadyToPlay ?
|
||||
<span>this is the world's worst loading screen and we shipped our software with it anyway... <br /><br />{loadStatusMessage}</span> :
|
||||
<VideoPlayer poster={metadata.thumbnail} autoplay={isPlaying} downloadPath={fileInfo.download_path} />) :
|
||||
<div className="video__cover" style={{backgroundImage: 'url("' + metadata.thumbnail + '")'}}>
|
||||
<WatchLink icon="icon-play" onWatchClick={this.onWatchClick.bind(this)}
|
||||
startPlaying={this.startPlaying.bind(this)} {...this.props}></WatchLink>
|
||||
<VideoPlayButton startPlaying={this.startPlaying.bind(this)} {...this.props} />
|
||||
</div>
|
||||
}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoPlayer extends React.PureComponent {
|
||||
class VideoPlayer extends React.Component {
|
||||
componentDidMount() {
|
||||
const elem = this.refs.video
|
||||
const {
|
||||
autoplay,
|
||||
downloadPath,
|
||||
contentType,
|
||||
} = this.props
|
||||
const players = plyr.setup(elem)
|
||||
players[0].play()
|
||||
if (autoplay) {
|
||||
players[0].play()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
downloadPath,
|
||||
contentType,
|
||||
poster,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<video controls id="video" ref="video">
|
||||
<video controls id="video" ref="video" style={{backgroundImage: "url('" + poster + "')"}} >
|
||||
<source src={downloadPath} type={contentType} />
|
||||
</video>
|
||||
)
|
||||
|
|
|
@ -99,10 +99,18 @@ class WunderBar extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._input.value = this.state.address;
|
||||
if (this._input && this._focusPending) {
|
||||
this._input.select();
|
||||
this._focusPending = false;
|
||||
if (this._input) {
|
||||
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._focusPending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 RESOLVE_URI_STARTED = 'RESOLVE_URI_STARTED'
|
||||
export const RESOLVE_URI_COMPLETED = 'RESOLVE_URI_COMPLETED'
|
||||
export const FETCH_DOWNLOADED_CONTENT_STARTED = 'FETCH_DOWNLOADED_CONTENT_STARTED'
|
||||
export const FETCH_DOWNLOADED_CONTENT_COMPLETED = 'FETCH_DOWNLOADED_CONTENT_COMPLETED'
|
||||
export const FETCH_PUBLISHED_CONTENT_STARTED = 'FETCH_PUBLISHED_CONTENT_STARTED'
|
||||
export const FETCH_PUBLISHED_CONTENT_COMPLETED = 'FETCH_PUBLISHED_CONTENT_COMPLETED'
|
||||
export const RESOLVE_URI_CANCELED = 'RESOLVE_URI_CANCELED'
|
||||
export const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED'
|
||||
export const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_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_COMPLETED = 'FETCH_FILE_INFO_COMPLETED'
|
||||
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 FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED'
|
||||
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED'
|
||||
export const DELETE_FILE_STARTED = 'DELETE_FILE_STARTED'
|
||||
export const DELETE_FILE_COMPLETED = 'DELETE_FILE_COMPLETED'
|
||||
export const FETCH_MY_CLAIMS_COMPLETED = 'FETCH_MY_CLAIMS_COMPLETED'
|
||||
export const FILE_DELETE = 'FILE_DELETE'
|
||||
|
||||
// Search
|
||||
export const SEARCH_STARTED = 'SEARCH_STARTED'
|
||||
export const SEARCH_COMPLETED = 'SEARCH_COMPLETED'
|
||||
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);
|
||||
|
||||
return xhr
|
||||
};
|
||||
|
||||
export default jsonrpc;
|
||||
|
|
293
ui/js/lbry.js
293
ui/js/lbry.js
|
@ -7,6 +7,25 @@ import {getLocal, getSession, setSession, setLocal} from './utils.js';
|
|||
const {remote, ipcRenderer} = require('electron');
|
||||
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
|
||||
* 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));
|
||||
}
|
||||
|
||||
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
|
||||
* removes them from the list.
|
||||
*/
|
||||
function getPendingPublishes() {
|
||||
lbry.getPendingPublishes = function() {
|
||||
const pendingPublishes = getLocal('pendingPublishes') || [];
|
||||
const newPendingPublishes = pendingPublishes.filter(pub => Date.now() - pub.time <= lbry.pendingPublishTimeout);
|
||||
setLocal('pendingPublishes', newPendingPublishes);
|
||||
|
@ -59,7 +78,7 @@ function getPendingPublishes() {
|
|||
* provided along withe the name. If no pending publish is found, returns null.
|
||||
*/
|
||||
function getPendingPublish({name, channel_name, outpoint}) {
|
||||
const pendingPublishes = getPendingPublishes();
|
||||
const pendingPublishes = lbry.getPendingPublishes();
|
||||
return pendingPublishes.find(
|
||||
pub => pub.outpoint === outpoint || (pub.name === name && (!channel_name || pub.channel_name === channel_name))
|
||||
) || null;
|
||||
|
@ -74,28 +93,8 @@ function pendingPublishToDummyFileInfo({name, outpoint, claim_id}) {
|
|||
}
|
||||
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) {
|
||||
jsonrpc.call(lbry.daemonConnectionString, method, params, callback, errorCallback, connectFailedCallback);
|
||||
return jsonrpc.call(lbry.daemonConnectionString, method, params, callback, errorCallback, connectFailedCallback);
|
||||
}
|
||||
|
||||
//core
|
||||
|
@ -151,69 +150,14 @@ lbry.isDaemonAcceptingConnections = function (callback) {
|
|||
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.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.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
|
||||
* Lighthouse. If Lighthouse can't be reached, it just retrives the
|
||||
|
@ -251,83 +195,22 @@ lbry.getCostInfo = function(uri) {
|
|||
}, 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 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) => {
|
||||
if (size) {
|
||||
getCost(name, size);
|
||||
}
|
||||
|
||||
lighthouse.get_size_for_name(name).then((size) => {
|
||||
if (size) {
|
||||
getCost(name, size);
|
||||
}
|
||||
else {
|
||||
getCost(name, null);
|
||||
}
|
||||
}, () => {
|
||||
else {
|
||||
getCost(name, null);
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
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
|
||||
* lbry.file_list() during the publish process.
|
||||
|
@ -368,10 +251,6 @@ lbry.publish = function(params, fileListedCallback, publishedCallback, errorCall
|
|||
fileListedCallback(true);
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
//lbry.getFileInfoWhenListed(params.name, function(fileInfo) {
|
||||
// fileListedCallback(fileInfo);
|
||||
//});
|
||||
}
|
||||
|
||||
|
||||
|
@ -426,29 +305,10 @@ lbry.formatName = function(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)
|
||||
{
|
||||
return lbry.rootPath + '/img/' + file;
|
||||
return 'img/' + file;
|
||||
}
|
||||
|
||||
lbry.getMediaType = function(contentType, fileName) {
|
||||
|
@ -479,71 +339,9 @@ lbry.stop = function(callback) {
|
|||
lbry.call('stop', {}, callback);
|
||||
};
|
||||
|
||||
lbry.fileInfo = {};
|
||||
lbry._subscribeIdCount = 0;
|
||||
lbry._fileInfoSubscribeCallbacks = {};
|
||||
lbry._fileInfoSubscribeInterval = 500000;
|
||||
lbry._balanceSubscribeCallbacks = {};
|
||||
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._updateBalanceSubscribers = function() {
|
||||
|
@ -620,7 +418,7 @@ lbry.file_list = function(params={}) {
|
|||
lbry.call('file_list', params, (fileInfos) => {
|
||||
removePendingPublishIfNeeded({name, channel_name, outpoint});
|
||||
|
||||
const dummyFileInfos = getPendingPublishes().map(pendingPublishToDummyFileInfo);
|
||||
const dummyFileInfos = lbry.getPendingPublishes().map(pendingPublishToDummyFileInfo);
|
||||
resolve([...fileInfos, ...dummyFileInfos]);
|
||||
}, reject, reject);
|
||||
});
|
||||
|
@ -633,14 +431,15 @@ lbry.claim_list_mine = function(params={}) {
|
|||
removePendingPublishIfNeeded({name, channel_name, outpoint: txid + ':' + nout});
|
||||
}
|
||||
|
||||
const dummyClaims = getPendingPublishes().map(pendingPublishToDummyClaim);
|
||||
const dummyClaims = lbry.getPendingPublishes().map(pendingPublishToDummyClaim);
|
||||
resolve([...claims, ...dummyClaims]);
|
||||
}, reject, reject)
|
||||
});
|
||||
}
|
||||
|
||||
const claimCacheKey = 'resolve_claim_cache';
|
||||
lbry._claimCache = getLocal(claimCacheKey, {});
|
||||
lbry._claimCache = getSession(claimCacheKey, {});
|
||||
lbry._resolveXhrs = {}
|
||||
lbry.resolve = function(params={}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!params.uri) {
|
||||
|
@ -649,28 +448,22 @@ lbry.resolve = function(params={}) {
|
|||
if (params.uri && lbry._claimCache[params.uri] !== undefined) {
|
||||
resolve(lbry._claimCache[params.uri]);
|
||||
} else {
|
||||
lbry.call('resolve', params, function(data) {
|
||||
lbry._claimCache[params.uri] = data;
|
||||
setLocal(claimCacheKey, lbry._claimCache)
|
||||
lbry._resolveXhrs[params.uri] = lbry.call('resolve', params, function(data) {
|
||||
if (data !== undefined) {
|
||||
lbry._claimCache[params.uri] = data;
|
||||
}
|
||||
setSession(claimCacheKey, lbry._claimCache)
|
||||
resolve(data)
|
||||
}, reject)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Adds caching.
|
||||
lbry._settingsPromise = null;
|
||||
lbry.settings_get = function(params={}) {
|
||||
if (params.allow_cached && lbry._settingsPromise) {
|
||||
return lbry._settingsPromise;
|
||||
lbry.cancelResolve = function(params={}) {
|
||||
const xhr = lbry._resolveXhrs[params.uri]
|
||||
if (xhr && xhr.readyState > 0 && xhr.readyState < 4) {
|
||||
xhr.abort()
|
||||
}
|
||||
lbry._settingsPromise = new Promise((resolve, reject) => {
|
||||
lbry.call('settings_get', {}, (settings) => {
|
||||
setSession('settings', settings);
|
||||
resolve(settings);
|
||||
}, reject);
|
||||
});
|
||||
return lbry._settingsPromise;
|
||||
}
|
||||
|
||||
// lbry.get = function(params={}) {
|
||||
|
|
|
@ -13,22 +13,19 @@ const lbryio = {
|
|||
const CONNECTION_STRING = 'https://api.lbry.io/';
|
||||
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
||||
|
||||
lbryio._exchangePromise = null;
|
||||
lbryio._exchangeLastFetched = null;
|
||||
lbryio.getExchangeRates = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const cached = getSession('exchangeRateCache');
|
||||
if (!cached || Date.now() - cached.time > EXCHANGE_RATE_TIMEOUT) {
|
||||
if (!lbryio._exchangeLastFetched || Date.now() - lbryio._exchangeLastFetched > EXCHANGE_RATE_TIMEOUT) {
|
||||
lbryio._exchangePromise = new Promise((resolve, reject) => {
|
||||
lbryio.call('lbc', 'exchange_rate', {}, 'get', true).then(({lbc_usd, lbc_btc, btc_usd}) => {
|
||||
const rates = {lbc_usd, lbc_btc, btc_usd};
|
||||
setSession('exchangeRateCache', {
|
||||
rates: rates,
|
||||
time: Date.now(),
|
||||
});
|
||||
resolve(rates);
|
||||
});
|
||||
} else {
|
||||
resolve(cached.rates);
|
||||
}
|
||||
});
|
||||
}).catch(reject);
|
||||
});
|
||||
lbryio._exchangeLastFetched = Date.now();
|
||||
}
|
||||
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
|
||||
|
|
|
@ -165,5 +165,30 @@ lbryuri.normalize= function(uri) {
|
|||
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;
|
||||
export default lbryuri;
|
||||
|
|
|
@ -10,9 +10,16 @@ import {AuthOverlay} from './component/auth.js';
|
|||
import { Provider } from 'react-redux';
|
||||
import store from 'store.js';
|
||||
import {
|
||||
doDaemonReady,
|
||||
doChangePath,
|
||||
doDaemonReady,
|
||||
doHistoryPush
|
||||
} from 'actions/app'
|
||||
import {
|
||||
doFetchDaemonSettings
|
||||
} from 'actions/settings'
|
||||
import {
|
||||
doFileList
|
||||
} from 'actions/file_info'
|
||||
import parseQueryParams from 'util/query_params'
|
||||
|
||||
const {remote, ipcRenderer} = require('electron');
|
||||
|
@ -27,16 +34,22 @@ window.addEventListener('contextmenu', (event) => {
|
|||
event.preventDefault();
|
||||
});
|
||||
|
||||
window.addEventListener('popstate', (event) => {
|
||||
const pathname = document.location.pathname
|
||||
window.addEventListener('popstate', (event, param) => {
|
||||
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) => {
|
||||
console.log('FIX ME do magic dispatch');
|
||||
if (uri) {
|
||||
console.log('FIX ME do magic dispatch: ' + uri);
|
||||
}
|
||||
});
|
||||
|
||||
const initialState = app.store.getState();
|
||||
|
@ -46,7 +59,9 @@ var init = function() {
|
|||
function onDaemonReady() {
|
||||
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.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)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,31 @@ import {
|
|||
connect
|
||||
} from 'react-redux'
|
||||
import {
|
||||
selectCurrentUriTitle,
|
||||
} from 'selectors/app'
|
||||
doFetchClaimsByChannel
|
||||
} from 'actions/content'
|
||||
import {
|
||||
makeSelectClaimForUri,
|
||||
makeSelectClaimsInChannelForUri
|
||||
} from 'selectors/claims'
|
||||
import ChannelPage from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
title: selectCurrentUriTitle(state)
|
||||
})
|
||||
import FilePage from './view'
|
||||
|
||||
const makeSelect = () => {
|
||||
const selectClaim = makeSelectClaimForUri(),
|
||||
selectClaimsInChannel = makeSelectClaimsInChannelForUri()
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: selectClaim(state, props),
|
||||
claimsInChannel: selectClaimsInChannel(state, props)
|
||||
})
|
||||
|
||||
return select
|
||||
}
|
||||
|
||||
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,22 +1,49 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri'
|
||||
|
||||
const ChannelPage = (props) => {
|
||||
const {
|
||||
title
|
||||
} = props
|
||||
class ChannelPage extends React.Component{
|
||||
componentDidMount() {
|
||||
this.fetchClaims(this.props)
|
||||
}
|
||||
|
||||
return <main className="main--single-column">
|
||||
<section className="card">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-identity"><h1>{title}</h1></div>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<p>
|
||||
This channel page is a stub.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.fetchClaims(nextProps)
|
||||
}
|
||||
|
||||
fetchClaims(props) {
|
||||
if (props.claimsInChannel === undefined) {
|
||||
props.fetchClaims(props.uri)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
claimsInChannel,
|
||||
claim,
|
||||
uri
|
||||
} = this.props
|
||||
|
||||
console.log(claimsInChannel);
|
||||
return <main className="main--single-column">
|
||||
<section className="card">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-identity"><h1>{uri}</h1></div>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<p>
|
||||
This channel page is a stub.
|
||||
</p>
|
||||
</div>
|
||||
</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>
|
||||
}
|
||||
}
|
||||
|
||||
export default ChannelPage;
|
||||
|
|
|
@ -3,13 +3,11 @@ import {
|
|||
connect
|
||||
} from 'react-redux'
|
||||
import {
|
||||
doFetchDownloadedContent,
|
||||
} from 'actions/content'
|
||||
doFetchFileInfosAndPublishedClaims,
|
||||
} from 'actions/file_info'
|
||||
import {
|
||||
selectFetchingDownloadedContent,
|
||||
} from 'selectors/content'
|
||||
import {
|
||||
selectDownloadedFileInfo,
|
||||
selectFileInfosDownloaded,
|
||||
selectFileListDownloadedOrPublishedIsPending,
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
doNavigate,
|
||||
|
@ -17,13 +15,13 @@ import {
|
|||
import FileListDownloaded from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
downloadedContent: selectDownloadedFileInfo(state),
|
||||
fetching: selectFetchingDownloadedContent(state),
|
||||
fileInfos: selectFileInfosDownloaded(state),
|
||||
isPending: selectFileListDownloadedOrPublishedIsPending(state),
|
||||
})
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
navigate: (path) => dispatch(doNavigate(path)),
|
||||
fetchFileListDownloaded: () => dispatch(doFetchDownloadedContent()),
|
||||
fetchFileInfosDownloaded: () => dispatch(doFetchFileInfosAndPublishedClaims()),
|
||||
})
|
||||
|
||||
export default connect(select, perform)(FileListDownloaded)
|
||||
|
|
|
@ -12,23 +12,25 @@ import SubHeader from 'component/subHeader'
|
|||
|
||||
class FileListDownloaded extends React.Component {
|
||||
componentWillMount() {
|
||||
this.props.fetchFileListDownloaded()
|
||||
this.props.fetchFileInfosDownloaded()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
downloadedContent,
|
||||
fetching,
|
||||
fileInfos,
|
||||
isPending,
|
||||
navigate,
|
||||
} = this.props
|
||||
|
||||
let content
|
||||
if (fetching) {
|
||||
content = <BusyMessage message="Loading" />
|
||||
} 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>
|
||||
if (fileInfos && fileInfos.length > 0) {
|
||||
content = <FileList fileInfos={fileInfos} fetching={isPending} />
|
||||
} 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 (
|
||||
|
|
|
@ -3,13 +3,11 @@ import {
|
|||
connect
|
||||
} from 'react-redux'
|
||||
import {
|
||||
doFetchPublishedContent,
|
||||
} from 'actions/content'
|
||||
doFetchFileInfosAndPublishedClaims,
|
||||
} from 'actions/file_info'
|
||||
import {
|
||||
selectFetchingPublishedContent,
|
||||
} from 'selectors/content'
|
||||
import {
|
||||
selectPublishedFileInfo,
|
||||
selectFileInfosPublished,
|
||||
selectFileListDownloadedOrPublishedIsPending
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
doNavigate,
|
||||
|
@ -17,13 +15,13 @@ import {
|
|||
import FileListPublished from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
publishedContent: selectPublishedFileInfo(state),
|
||||
fetching: selectFetchingPublishedContent(state),
|
||||
fileInfos: selectFileInfosPublished(state),
|
||||
isPending: selectFileListDownloadedOrPublishedIsPending(state),
|
||||
})
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
navigate: (path) => dispatch(doNavigate(path)),
|
||||
fetchFileListPublished: () => dispatch(doFetchPublishedContent()),
|
||||
fetchFileListPublished: () => dispatch(doFetchFileInfosAndPublishedClaims()),
|
||||
})
|
||||
|
||||
export default connect(select, perform)(FileListPublished)
|
||||
|
|
|
@ -3,7 +3,7 @@ import lbry from 'lbry.js';
|
|||
import lbryuri from 'lbryuri.js';
|
||||
import Link from 'component/link';
|
||||
import {FormField} from 'component/form.js';
|
||||
import {FileTile} from 'component/fileTile';
|
||||
import FileTile from 'component/fileTile';
|
||||
import rewards from 'rewards.js';
|
||||
import lbryio from 'lbryio.js';
|
||||
import {BusyMessage, Thumbnail} from 'component/common.js';
|
||||
|
@ -16,7 +16,7 @@ class FileListPublished extends React.Component {
|
|||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if(this.props.publishedContent.length > 0) this._requestPublishReward()
|
||||
if(this.props.fileInfos.length > 0) this._requestPublishReward()
|
||||
}
|
||||
|
||||
_requestPublishReward() {
|
||||
|
@ -38,18 +38,21 @@ class FileListPublished extends React.Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
publishedContent,
|
||||
fetching,
|
||||
fileInfos,
|
||||
isPending,
|
||||
navigate,
|
||||
} = this.props
|
||||
|
||||
let content
|
||||
if (fetching) {
|
||||
content = <BusyMessage message="Loading" />
|
||||
} else if (!publishedContent.length) {
|
||||
content = <span>You haven't downloaded anything from LBRY yet. Go <Link onClick={() => navigate('/discover')} label="search for your first download" />!</span>
|
||||
|
||||
if (fileInfos && fileInfos.length > 0) {
|
||||
content = <FileList fileInfos={fileInfos} fetching={isPending} fileTileShowEmpty={FileTile.SHOW_EMPTY_PENDING} />
|
||||
} 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 (
|
||||
|
|
|
@ -3,29 +3,41 @@ import {
|
|||
connect
|
||||
} from 'react-redux'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
doFetchFileInfo,
|
||||
} from 'actions/file_info'
|
||||
import {
|
||||
selectCurrentUriFileInfo,
|
||||
selectCurrentUriIsDownloaded,
|
||||
makeSelectFileInfoForUri,
|
||||
} from 'selectors/file_info'
|
||||
import {
|
||||
selectCurrentUriClaim,
|
||||
makeSelectClaimForUri,
|
||||
makeSelectContentTypeForUri,
|
||||
makeSelectMetadataForUri,
|
||||
} from 'selectors/claims'
|
||||
import {
|
||||
selectCurrentUriCostInfo,
|
||||
makeSelectCostInfoForUri,
|
||||
} from 'selectors/cost_info'
|
||||
import FilePage from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
claim: selectCurrentUriClaim(state),
|
||||
uri: selectCurrentUri(state),
|
||||
isDownloaded: selectCurrentUriIsDownloaded(state),
|
||||
fileInfo: selectCurrentUriFileInfo(state),
|
||||
costInfo: selectCurrentUriCostInfo(state),
|
||||
})
|
||||
const makeSelect = () => {
|
||||
const selectClaim = makeSelectClaimForUri(),
|
||||
selectContentType = makeSelectContentTypeForUri(),
|
||||
selectFileInfo = makeSelectFileInfoForUri(),
|
||||
selectCostInfo = makeSelectCostInfoForUri(),
|
||||
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) => ({
|
||||
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 lbry from 'lbry.js';
|
||||
import lighthouse from 'lighthouse.js';
|
||||
import lbryuri from 'lbryuri.js';
|
||||
import Video from 'component/video'
|
||||
import {
|
||||
TruncatedText,
|
||||
Thumbnail,
|
||||
BusyMessage,
|
||||
} from 'component/common';
|
||||
import FilePrice from 'component/filePrice'
|
||||
import FileActions from 'component/fileActions';
|
||||
|
@ -45,87 +42,88 @@ const FormatItem = (props) => {
|
|||
)
|
||||
}
|
||||
|
||||
const FilePage = (props) => {
|
||||
const {
|
||||
claim,
|
||||
navigate,
|
||||
claim: {
|
||||
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 {
|
||||
claim,
|
||||
fileInfo,
|
||||
metadata,
|
||||
contentType,
|
||||
uri,
|
||||
} = this.props
|
||||
|
||||
if (!claim || !metadata) {
|
||||
return <span className="empty">Empty claim or metadata info.</span>
|
||||
}
|
||||
|
||||
const {
|
||||
txid,
|
||||
nout,
|
||||
has_signature: hasSignature,
|
||||
signature_is_valid: signatureIsValid,
|
||||
value,
|
||||
value: {
|
||||
stream,
|
||||
stream: {
|
||||
metadata,
|
||||
source,
|
||||
metadata: {
|
||||
title,
|
||||
} = {},
|
||||
source: {
|
||||
contentType,
|
||||
} = {},
|
||||
} = {},
|
||||
} = {},
|
||||
},
|
||||
uri,
|
||||
isDownloaded,
|
||||
fileInfo,
|
||||
costInfo,
|
||||
costInfo: {
|
||||
cost,
|
||||
includesData: costIncludesData,
|
||||
} = {},
|
||||
} = props
|
||||
value
|
||||
} = claim
|
||||
|
||||
const outpoint = txid + ':' + nout;
|
||||
const uriLookupComplete = !!claim && Object.keys(claim).length
|
||||
const outpoint = txid + ':' + nout
|
||||
const title = metadata.title
|
||||
const channelUriObj = lbryuri.parse(uri)
|
||||
delete channelUriObj.path;
|
||||
delete channelUriObj.contentName;
|
||||
const channelUri = signatureIsValid && hasSignature && channelUriObj.isChannel ? lbryuri.build(channelUriObj, false) : null
|
||||
const uriIndicator = <UriIndicator uri={uri} />
|
||||
|
||||
const channelUriObj = lbryuri.parse(uri)
|
||||
delete channelUriObj.path;
|
||||
delete channelUriObj.contentName;
|
||||
const channelUri = signatureIsValid && hasSignature && channelUriObj.isChannel ? lbryuri.build(channelUriObj, false) : null;
|
||||
const uriIndicator = <UriIndicator uri={uri} />
|
||||
|
||||
// <p>This location is not yet in use. { ' ' }<Link onClick={() => navigate('/publish')} label="Put something here" />.</p>
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<section className="show-page-media">
|
||||
{ contentType && contentType.startsWith('video/') ?
|
||||
<Video className="video-embedded" uri={uri} metadata={metadata} outpoint={outpoint} /> :
|
||||
(Object.keys(metadata).length > 0 ? <Thumbnail src={metadata.thumbnail} /> : <Thumbnail />) }
|
||||
</section>
|
||||
<section className="card">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-identity">
|
||||
{isDownloaded === false
|
||||
? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(uri)} /></span>
|
||||
: null}
|
||||
<h1>{title}</h1>
|
||||
<div className="card__subtitle">
|
||||
{ channelUri ?
|
||||
<Link href={"?show=" + channelUri }>{uriIndicator}</Link> :
|
||||
uriIndicator}
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
<FileActions uri={uri} /></div>
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<section className="show-page-media">
|
||||
{ contentType && contentType.startsWith('video/') ?
|
||||
<Video className="video-embedded" uri={uri} /> :
|
||||
(metadata && metadata.thumbnail ? <Thumbnail src={metadata.thumbnail} /> : <Thumbnail />) }
|
||||
</section>
|
||||
<section className="card">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-identity">
|
||||
{!fileInfo || fileInfo.written_bytes <= 0
|
||||
? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(uri)} /></span>
|
||||
: null}<h1>{title}</h1>
|
||||
<div className="card__subtitle">
|
||||
{ channelUri ?
|
||||
<Link href={"?show=" + channelUri }>{uriIndicator}</Link> :
|
||||
uriIndicator}
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
<FileActions uri={uri} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
||||
{metadata && metadata.description}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
||||
{metadata.description}
|
||||
</div>
|
||||
</div>
|
||||
{ metadata ?
|
||||
{ metadata ?
|
||||
<div className="card__content">
|
||||
<FormatItem metadata={metadata} contentType={contentType} />
|
||||
</div> : '' }
|
||||
<div className="card__content">
|
||||
<FormatItem metadata={metadata} contentType={contentType} cost={cost} uri={uri} outpoint={outpoint} costIncludesData={costIncludesData} />
|
||||
</div> : '' }
|
||||
<div className="card__content">
|
||||
<Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
<Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default FilePage;
|
|
@ -24,9 +24,6 @@ var HelpPage = React.createClass({
|
|||
});
|
||||
});
|
||||
},
|
||||
componentDidMount: function() {
|
||||
document.title = "Help";
|
||||
},
|
||||
render: function() {
|
||||
let ver, osName, platform, newVerLink;
|
||||
if (this.state.versionInfo) {
|
||||
|
|
|
@ -5,9 +5,13 @@ import {
|
|||
import {
|
||||
doNavigate,
|
||||
} from 'actions/app'
|
||||
import {
|
||||
selectMyClaims
|
||||
} from 'selectors/claims'
|
||||
import PublishPage from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
myClaims: selectMyClaims(state)
|
||||
})
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import lbryuri from 'lbryuri'
|
||||
import {FormField, FormRow} from 'component/form.js';
|
||||
import Link from 'component/link';
|
||||
import rewards from 'rewards';
|
||||
|
@ -100,7 +101,7 @@ var PublishPage = React.createClass({
|
|||
};
|
||||
|
||||
if (this.state.isFee) {
|
||||
lbry.getUnusedAddress((address) => {
|
||||
lbry.wallet_unused_address().then((address) => {
|
||||
metadata.fee = {};
|
||||
metadata.fee[this.state.feeCurrency] = {
|
||||
amount: parseFloat(this.state.feeAmount),
|
||||
|
@ -169,7 +170,7 @@ var PublishPage = React.createClass({
|
|||
return;
|
||||
}
|
||||
|
||||
if (!lbry.nameIsValid(rawName, false)) {
|
||||
if (!lbryuri.isValidName(rawName, false)) {
|
||||
this.refs.name.showError('LBRY names must contain only letters, numbers and dashes.');
|
||||
return;
|
||||
}
|
||||
|
@ -182,50 +183,45 @@ var PublishPage = React.createClass({
|
|||
myClaimExists: null,
|
||||
});
|
||||
|
||||
lbry.getMyClaim(name, (myClaimInfo) => {
|
||||
const myClaimInfo = Object.values(this.props.myClaims).find(claim => claim.name === name)
|
||||
|
||||
this.setState({
|
||||
myClaimExists: !!myClaimInfo,
|
||||
});
|
||||
lbry.resolve({uri: name}).then((claimInfo) => {
|
||||
if (name != this.state.name) {
|
||||
// A new name has been typed already, so bail
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
myClaimExists: !!myClaimInfo,
|
||||
});
|
||||
lbry.resolve({uri: name}).then((claimInfo) => {
|
||||
if (name != this.state.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!claimInfo) {
|
||||
this.setState({
|
||||
nameResolved: false,
|
||||
});
|
||||
} else {
|
||||
const topClaimIsMine = (myClaimInfo && myClaimInfo.claim.amount >= claimInfo.claim.amount);
|
||||
const newState = {
|
||||
nameResolved: true,
|
||||
topClaimValue: parseFloat(claimInfo.claim.amount),
|
||||
myClaimExists: !!myClaimInfo,
|
||||
myClaimValue: myClaimInfo ? parseFloat(myClaimInfo.claim.amount) : null,
|
||||
myClaimMetadata: myClaimInfo ? myClaimInfo.value : null,
|
||||
topClaimIsMine: topClaimIsMine,
|
||||
};
|
||||
|
||||
if (topClaimIsMine) {
|
||||
newState.bid = myClaimInfo.claim.amount;
|
||||
} else if (this.state.myClaimMetadata) {
|
||||
// Just changed away from a name we have a claim on, so clear pre-fill
|
||||
newState.bid = '';
|
||||
}
|
||||
|
||||
this.setState(newState);
|
||||
}
|
||||
}, () => { // Assume an error means the name is available
|
||||
if (!claimInfo) {
|
||||
this.setState({
|
||||
name: name,
|
||||
nameResolved: false,
|
||||
myClaimExists: false,
|
||||
});
|
||||
} else {
|
||||
const topClaimIsMine = (myClaimInfo && myClaimInfo.claim.amount >= claimInfo.claim.amount);
|
||||
const newState = {
|
||||
nameResolved: true,
|
||||
topClaimValue: parseFloat(claimInfo.claim.amount),
|
||||
myClaimExists: !!myClaimInfo,
|
||||
myClaimValue: myClaimInfo ? parseFloat(myClaimInfo.claim.amount) : null,
|
||||
myClaimMetadata: myClaimInfo ? myClaimInfo.value : null,
|
||||
topClaimIsMine: topClaimIsMine,
|
||||
};
|
||||
|
||||
if (topClaimIsMine) {
|
||||
newState.bid = myClaimInfo.claim.amount;
|
||||
} else if (this.state.myClaimMetadata) {
|
||||
// Just changed away from a name we have a claim on, so clear pre-fill
|
||||
newState.bid = '';
|
||||
}
|
||||
|
||||
this.setState(newState);
|
||||
}
|
||||
}, () => { // Assume an error means the name is available
|
||||
this.setState({
|
||||
name: name,
|
||||
nameResolved: false,
|
||||
myClaimExists: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -287,7 +283,7 @@ var PublishPage = React.createClass({
|
|||
handleNewChannelNameChange: function (event) {
|
||||
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.');
|
||||
return;
|
||||
} else {
|
||||
|
|
|
@ -8,14 +8,15 @@ import {BusyMessage} from 'component/common.js';
|
|||
|
||||
class SearchPage extends React.Component{
|
||||
render() {
|
||||
const isValidUri = (query) => true //FIXME
|
||||
console.log('render search page')
|
||||
|
||||
const {
|
||||
query,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
{ isValidUri(query) ?
|
||||
{ lbryuri.isValid(query) ?
|
||||
<section className="section-spaced">
|
||||
<h3 className="card-row__header">
|
||||
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 {FormField, FormRow} from '../component/form.js';
|
||||
import {FormField, FormRow} from 'component/form.js';
|
||||
import SubHeader from 'component/subHeader'
|
||||
import lbry from '../lbry.js';
|
||||
import lbry from 'lbry.js';
|
||||
|
||||
var SettingsPage = React.createClass({
|
||||
_onSettingSaveSuccess: function() {
|
||||
// This is bad.
|
||||
// document.dispatchEvent(new CustomEvent('globalNotice', {
|
||||
// detail: {
|
||||
// message: "Settings saved",
|
||||
// },
|
||||
// }))
|
||||
},
|
||||
setDaemonSetting: function(name, value) {
|
||||
lbry.setDaemonSetting(name, value, this._onSettingSaveSuccess)
|
||||
this.props.setDaemonSetting(name, value)
|
||||
},
|
||||
setClientSetting: function(name, value) {
|
||||
lbry.setClientSetting(name, value)
|
||||
|
@ -51,21 +43,15 @@ var SettingsPage = React.createClass({
|
|||
this.setDaemonSetting('max_download', Number(event.target.value));
|
||||
},
|
||||
getInitialState: function() {
|
||||
const daemonSettings = this.props.daemonSettings
|
||||
|
||||
return {
|
||||
settings: null,
|
||||
isMaxUpload: daemonSettings && daemonSettings.max_upload != 0,
|
||||
isMaxDownload: daemonSettings && daemonSettings.max_download != 0,
|
||||
showNsfw: lbry.getClientSetting('showNsfw'),
|
||||
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) {
|
||||
lbry.setClientSetting('showNsfw', event.target.checked);
|
||||
},
|
||||
|
@ -73,8 +59,12 @@ var SettingsPage = React.createClass({
|
|||
|
||||
},
|
||||
render: function() {
|
||||
if (!this.state.daemonSettings) {
|
||||
return null;
|
||||
const {
|
||||
daemonSettings
|
||||
} = this.props
|
||||
|
||||
if (!daemonSettings) {
|
||||
return <main className="main--single-column"><span className="empty">Failed to load settings.</span></main>;
|
||||
}
|
||||
/*
|
||||
<section className="card">
|
||||
|
@ -84,7 +74,7 @@ var SettingsPage = React.createClass({
|
|||
<div className="card__content">
|
||||
<FormRow type="checkbox"
|
||||
onChange={this.onRunOnStartChange}
|
||||
defaultChecked={this.state.daemonSettings.run_on_startup}
|
||||
defaultChecked={daemonSettings.run_on_startup}
|
||||
label="Run LBRY automatically when I start my computer" />
|
||||
</div>
|
||||
</section>
|
||||
|
@ -99,7 +89,7 @@ var SettingsPage = React.createClass({
|
|||
<div className="card__content">
|
||||
<FormRow type="directory"
|
||||
name="download_directory"
|
||||
defaultValue={this.state.daemonSettings.download_directory}
|
||||
defaultValue={daemonSettings.download_directory}
|
||||
helper="LBRY downloads will be saved here."
|
||||
onChange={this.onDownloadDirChange} />
|
||||
</div>
|
||||
|
@ -125,7 +115,7 @@ var SettingsPage = React.createClass({
|
|||
<FormField type="number"
|
||||
min="0"
|
||||
step=".5"
|
||||
defaultValue={this.state.daemonSettings.max_upload}
|
||||
defaultValue={daemonSettings.max_upload}
|
||||
placeholder="10"
|
||||
className="form-field__input--inline"
|
||||
onChange={this.onMaxUploadFieldChange}
|
||||
|
@ -153,7 +143,7 @@ var SettingsPage = React.createClass({
|
|||
<FormField type="number"
|
||||
min="0"
|
||||
step=".5"
|
||||
defaultValue={this.state.daemonSettings.max_download}
|
||||
defaultValue={daemonSettings.max_download}
|
||||
placeholder="10"
|
||||
className="form-field__input--inline"
|
||||
onChange={this.onMaxDownloadFieldChange}
|
||||
|
@ -188,7 +178,7 @@ var SettingsPage = React.createClass({
|
|||
<div className="card__content">
|
||||
<FormRow type="checkbox"
|
||||
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" />
|
||||
</div>
|
||||
</section>
|
|
@ -6,24 +6,27 @@ import {
|
|||
doResolveUri,
|
||||
} from 'actions/content'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
import {
|
||||
selectCurrentUriClaim,
|
||||
makeSelectClaimForUri,
|
||||
} from 'selectors/claims'
|
||||
import {
|
||||
selectCurrentUriIsResolving,
|
||||
makeSelectIsResolvingForUri,
|
||||
} from 'selectors/content'
|
||||
import ShowPage from './view'
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: selectCurrentUriClaim(state),
|
||||
uri: selectCurrentUri(state),
|
||||
isResolvingUri: selectCurrentUriIsResolving(state)
|
||||
})
|
||||
const makeSelect = () => {
|
||||
const selectClaim = makeSelectClaimForUri(),
|
||||
selectIsResolving = makeSelectIsResolvingForUri();
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: selectClaim(state, props),
|
||||
isResolvingUri: selectIsResolving(state, props)
|
||||
})
|
||||
|
||||
return select
|
||||
}
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
resolveUri: (uri) => dispatch(doResolveUri(uri))
|
||||
})
|
||||
|
||||
export default connect(select, perform)(ShowPage)
|
||||
export default connect(makeSelect, perform)(ShowPage)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri'
|
||||
import {
|
||||
BusyMessage,
|
||||
} from 'component/common';
|
||||
import ChannelPage from 'page/channel'
|
||||
import FilePage from 'page/filePage'
|
||||
|
||||
class ShowPage extends React.Component{
|
||||
|
@ -21,7 +23,7 @@ class ShowPage extends React.Component{
|
|||
uri,
|
||||
} = props
|
||||
|
||||
if(!isResolvingUri && !claim && uri) {
|
||||
if(!isResolvingUri && claim === undefined && uri) {
|
||||
resolveUri(uri)
|
||||
}
|
||||
}
|
||||
|
@ -35,22 +37,22 @@ class ShowPage extends React.Component{
|
|||
|
||||
let innerContent = "";
|
||||
|
||||
if (isResolvingUri) {
|
||||
if (isResolvingUri || !claim) {
|
||||
innerContent = <section className="card">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-identity"><h1>{uri}</h1></div>
|
||||
</div>
|
||||
<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>
|
||||
</section>;
|
||||
</section>
|
||||
}
|
||||
else if (claim && claim.whatever) {
|
||||
innerContent = "channel"
|
||||
// innerContent = <ChannelPage title={uri} />
|
||||
else if (claim.name.length && claim.name[0] === '@') {
|
||||
innerContent = <ChannelPage uri={lbryuri.build({ name: claim.name, claimId: claim.claim_id })} />
|
||||
}
|
||||
else if (claim) {
|
||||
innerContent = <FilePage />
|
||||
innerContent = <FilePage uri={uri} />
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -9,7 +9,6 @@ const defaultState = {
|
|||
upgradeSkipped: sessionStorage.getItem('upgradeSkipped'),
|
||||
daemonReady: false,
|
||||
obscureNsfw: !lbry.getClientSetting('showNsfw'),
|
||||
hidePrice: false,
|
||||
hasSignature: false,
|
||||
}
|
||||
|
||||
|
|
|
@ -9,10 +9,8 @@ reducers[types.FETCH_AVAILABILITY_STARTED] = function(state, action) {
|
|||
uri,
|
||||
} = action.data
|
||||
const newFetching = Object.assign({}, state.fetching)
|
||||
const newByUri = Object.assign({}, newFetching.byUri)
|
||||
|
||||
newByUri[uri] = true
|
||||
newFetching.byUri = newByUri
|
||||
newFetching[uri] = true
|
||||
|
||||
return Object.assign({}, state, {
|
||||
fetching: newFetching,
|
||||
|
@ -24,12 +22,11 @@ reducers[types.FETCH_AVAILABILITY_COMPLETED] = function(state, action) {
|
|||
uri,
|
||||
availability,
|
||||
} = action.data
|
||||
|
||||
const newFetching = Object.assign({}, state.fetching)
|
||||
const newFetchingByUri = Object.assign({}, newFetching.byUri)
|
||||
const newAvailabilityByUri = Object.assign({}, state.byUri)
|
||||
|
||||
delete newFetchingByUri[uri]
|
||||
newFetching.byUri = newFetchingByUri
|
||||
delete newFetching[uri]
|
||||
newAvailabilityByUri[uri] = availability
|
||||
|
||||
return Object.assign({}, state, {
|
||||
|
|
|
@ -8,30 +8,69 @@ const defaultState = {
|
|||
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
certificate,
|
||||
claim,
|
||||
} = action.data
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
|
||||
newByUri[uri] = claim
|
||||
const newClaims = Object.assign({}, state.claimsByUri)
|
||||
|
||||
newClaims[uri] = claim
|
||||
|
||||
//This needs a sanity boost...
|
||||
if (certificate !== undefined && claim === undefined) {
|
||||
const uriParts = lbryuri.parse(uri);
|
||||
// newChannelClaims[uri] = certificate
|
||||
if (claim === undefined) {
|
||||
newClaims[uri] = certificate
|
||||
}
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
byUri: newByUri,
|
||||
claimsByUri: newClaims
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FETCH_MY_CLAIMS_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
claims,
|
||||
} = action.data
|
||||
const newMine = Object.assign({}, state.mine)
|
||||
const newById = Object.assign({}, newMine.byId)
|
||||
|
||||
claims.forEach(claim => {
|
||||
newById[claim.claim_id] = claim
|
||||
reducers[types.RESOLVE_URI_CANCELED] = function(state, action) {
|
||||
const uri = action.data.uri
|
||||
const newClaims = Object.assign({}, state.claimsByUri)
|
||||
delete newClaims[uri]
|
||||
return Object.assign({}, state, {
|
||||
claimsByUri: newClaims
|
||||
})
|
||||
newMine.byId = newById
|
||||
}
|
||||
|
||||
|
||||
reducers[types.CLAIM_LIST_MINE_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
isClaimListMinePending: true
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
|
||||
const myClaims = Object.assign({}, state.myClaims)
|
||||
action.data.claims.forEach((claim) => {
|
||||
myClaims[claim.claim_id] = claim
|
||||
})
|
||||
return Object.assign({}, state, {
|
||||
isClaimListMinePending: false,
|
||||
myClaims: myClaims
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
claims
|
||||
} = action.data
|
||||
|
||||
const newClaims = Object.assign({}, state.claimsByChannel)
|
||||
|
||||
if (claims !== undefined) {
|
||||
newClaims[uri] = claims
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
mine: newMine,
|
||||
claimsByChannel: newClaims
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ reducers[types.RESOLVE_URI_STARTED] = function(state, action) {
|
|||
|
||||
const oldResolving = state.resolvingUris || []
|
||||
const newResolving = Object.assign([], oldResolving)
|
||||
if (newResolving.indexOf(uri) == -1) newResolving.push(uri)
|
||||
if (newResolving.indexOf(uri) === -1) newResolving.push(uri)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
resolvingUris: newResolving
|
||||
|
@ -48,51 +48,14 @@ reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
|||
...resolvingUris.slice(index + 1)
|
||||
]
|
||||
|
||||
const newState = Object.assign({}, state, {
|
||||
return Object.assign({}, state, {
|
||||
resolvingUris: newResolvingUris,
|
||||
})
|
||||
|
||||
return Object.assign({}, state, newState)
|
||||
}
|
||||
|
||||
reducers[types.FETCH_DOWNLOADED_CONTENT_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
fetchingDownloadedContent: true,
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FETCH_DOWNLOADED_CONTENT_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
fileInfos
|
||||
} = action.data
|
||||
const newDownloadedContent = Object.assign({}, state.downloadedContent, {
|
||||
fileInfos
|
||||
})
|
||||
|
||||
return Object.assign({}, state, {
|
||||
downloadedContent: newDownloadedContent,
|
||||
fetchingDownloadedContent: false,
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FETCH_PUBLISHED_CONTENT_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
fetchingPublishedContent: true,
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FETCH_PUBLISHED_CONTENT_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
fileInfos
|
||||
} = action.data
|
||||
const newPublishedContent = Object.assign({}, state.publishedContent, {
|
||||
fileInfos
|
||||
})
|
||||
|
||||
return Object.assign({}, state, {
|
||||
publishedContent: newPublishedContent,
|
||||
fetchingPublishedContent: false,
|
||||
})
|
||||
reducers[types.RESOLVE_URI_CANCELED] = function(state, action) {
|
||||
return reducers[types.RESOLVE_URI_COMPLETED](state, action)
|
||||
}
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
|
|
|
@ -5,13 +5,35 @@ const reducers = {}
|
|||
const defaultState = {
|
||||
}
|
||||
|
||||
reducers[types.FILE_LIST_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
isFileListPending: true,
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FILE_LIST_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
fileInfos,
|
||||
} = action.data
|
||||
|
||||
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||
fileInfos.forEach((fileInfo) => {
|
||||
newFileInfos[fileInfo.outpoint] = fileInfo
|
||||
})
|
||||
|
||||
return Object.assign({}, state, {
|
||||
isFileListPending: false,
|
||||
fileInfos: newFileInfos
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
outpoint
|
||||
} = action.data
|
||||
const newFetching = Object.assign({}, state.fetching)
|
||||
|
||||
newFetching[uri] = true
|
||||
newFetching[outpoint] = true
|
||||
|
||||
return Object.assign({}, state, {
|
||||
fetching: newFetching,
|
||||
|
@ -20,17 +42,18 @@ reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) {
|
|||
|
||||
reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
fileInfo,
|
||||
outpoint,
|
||||
} = action.data
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
|
||||
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||
const newFetching = Object.assign({}, state.fetching)
|
||||
|
||||
newByUri[uri] = fileInfo || {}
|
||||
delete newFetching[uri]
|
||||
newFileInfos[outpoint] = fileInfo
|
||||
delete newFetching[outpoint]
|
||||
|
||||
return Object.assign({}, state, {
|
||||
byUri: newByUri,
|
||||
fileInfos: newFileInfos,
|
||||
fetching: newFetching,
|
||||
})
|
||||
}
|
||||
|
@ -38,93 +61,74 @@ reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
|
|||
reducers[types.DOWNLOADING_STARTED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
outpoint,
|
||||
fileInfo,
|
||||
} = action.data
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
const newDownloading = Object.assign({}, state.downloading)
|
||||
const newDownloadingByUri = Object.assign({}, newDownloading.byUri)
|
||||
const newLoading = Object.assign({}, state.loading)
|
||||
const newLoadingByUri = Object.assign({}, newLoading)
|
||||
|
||||
newDownloadingByUri[uri] = true
|
||||
newDownloading.byUri = newDownloadingByUri
|
||||
newByUri[uri] = fileInfo
|
||||
delete newLoadingByUri[uri]
|
||||
newLoading.byUri = newLoadingByUri
|
||||
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||
const newDownloading = Object.assign({}, state.urisDownloading)
|
||||
const newLoading = Object.assign({}, state.urisLoading)
|
||||
|
||||
newDownloading[uri] = true
|
||||
newFileInfos[outpoint] = fileInfo
|
||||
delete newLoading[uri]
|
||||
|
||||
return Object.assign({}, state, {
|
||||
downloading: newDownloading,
|
||||
byUri: newByUri,
|
||||
loading: newLoading,
|
||||
urisDownloading: newDownloading,
|
||||
urisLoading: newLoading,
|
||||
fileInfos: newFileInfos,
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
outpoint,
|
||||
fileInfo,
|
||||
} = action.data
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
const newDownloading = Object.assign({}, state.downloading)
|
||||
|
||||
newByUri[uri] = fileInfo
|
||||
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||
const newDownloading = Object.assign({}, state.urisDownloading)
|
||||
|
||||
newFileInfos[outpoint] = fileInfo
|
||||
newDownloading[uri] = true
|
||||
|
||||
return Object.assign({}, state, {
|
||||
byUri: newByUri,
|
||||
downloading: newDownloading
|
||||
fileInfos: newFileInfos,
|
||||
urisDownloading: newDownloading
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
outpoint,
|
||||
fileInfo,
|
||||
} = action.data
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
const newDownloading = Object.assign({}, state.downloading)
|
||||
const newDownloadingByUri = Object.assign({}, newDownloading.byUri)
|
||||
|
||||
newByUri[uri] = fileInfo
|
||||
delete newDownloadingByUri[uri]
|
||||
newDownloading.byUri = newDownloadingByUri
|
||||
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||
const newDownloading = Object.assign({}, state.urisDownloading)
|
||||
|
||||
newFileInfos[outpoint] = fileInfo
|
||||
delete newDownloading[uri]
|
||||
|
||||
return Object.assign({}, state, {
|
||||
byUri: newByUri,
|
||||
downloading: newDownloading,
|
||||
fileInfos: newFileInfos,
|
||||
urisDownloading: newDownloading,
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.DELETE_FILE_STARTED] = function(state, action) {
|
||||
reducers[types.FILE_DELETE] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
outpoint,
|
||||
} = action.data
|
||||
const newDeleting = Object.assign({}, state.deleting)
|
||||
const newByUri = Object.assign({}, newDeleting.byUri)
|
||||
|
||||
newByUri[uri] = true
|
||||
newDeleting.byUri = newByUri
|
||||
const newFileInfos = Object.assign({}, state.fileInfos)
|
||||
|
||||
delete newFileInfos[outpoint]
|
||||
|
||||
return Object.assign({}, state, {
|
||||
deleting: newDeleting,
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.DELETE_FILE_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
} = action.data
|
||||
const newDeleting = Object.assign({}, state.deleting)
|
||||
const newDeletingByUri = Object.assign({}, newDeleting.byUri)
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
|
||||
delete newDeletingByUri[uri]
|
||||
newDeleting.byUri = newDeletingByUri
|
||||
delete newByUri[uri]
|
||||
|
||||
return Object.assign({}, state, {
|
||||
deleting: newDeleting,
|
||||
byUri: newByUri,
|
||||
fileInfos: newFileInfos,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -132,14 +136,13 @@ reducers[types.LOADING_VIDEO_STARTED] = function(state, action) {
|
|||
const {
|
||||
uri,
|
||||
} = action.data
|
||||
const newLoading = Object.assign({}, state.loading)
|
||||
const newByUri = Object.assign({}, newLoading.byUri)
|
||||
|
||||
newByUri[uri] = true
|
||||
newLoading.byUri = newByUri
|
||||
const newLoading = Object.assign({}, state.urisLoading)
|
||||
|
||||
newLoading[uri] = true
|
||||
|
||||
return Object.assign({}, state, {
|
||||
loading: newLoading,
|
||||
urisLoading: newLoading,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -147,54 +150,13 @@ reducers[types.LOADING_VIDEO_FAILED] = function(state, action) {
|
|||
const {
|
||||
uri,
|
||||
} = action.data
|
||||
const newLoading = Object.assign({}, state.loading)
|
||||
const newByUri = Object.assign({}, newLoading.byUri)
|
||||
|
||||
delete newByUri[uri]
|
||||
newLoading.byUri = newByUri
|
||||
const newLoading = Object.assign({}, state.urisLoading)
|
||||
|
||||
delete newLoading[uri]
|
||||
|
||||
return Object.assign({}, state, {
|
||||
loading: newLoading,
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FETCH_DOWNLOADED_CONTENT_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
fileInfos,
|
||||
} = action.data
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
|
||||
fileInfos.forEach(fileInfo => {
|
||||
const uri = lbryuri.build({
|
||||
channelName: fileInfo.channel_name,
|
||||
contentName: fileInfo.name,
|
||||
})
|
||||
|
||||
newByUri[uri] = fileInfo
|
||||
})
|
||||
|
||||
return Object.assign({}, state, {
|
||||
byUri: newByUri
|
||||
})
|
||||
}
|
||||
|
||||
reducers[types.FETCH_PUBLISHED_CONTENT_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
fileInfos
|
||||
} = action.data
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
|
||||
fileInfos.forEach(fileInfo => {
|
||||
const uri = lbryuri.build({
|
||||
channelName: fileInfo.channel_name,
|
||||
contentName: fileInfo.name,
|
||||
})
|
||||
|
||||
newByUri[uri] = fileInfo
|
||||
})
|
||||
|
||||
return Object.assign({}, state, {
|
||||
byUri: newByUri
|
||||
urisLoading: newLoading,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +1,14 @@
|
|||
import * as types from 'constants/action_types'
|
||||
|
||||
const reducers = {}
|
||||
const defaultState = {
|
||||
}
|
||||
const defaultState = {}
|
||||
|
||||
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
uri,
|
||||
certificate,
|
||||
} = action.data
|
||||
if (!certificate) return state
|
||||
|
||||
const newByUri = Object.assign({}, state.byUri)
|
||||
|
||||
newByUri[uri] = certificate
|
||||
reducers[types.DAEMON_SETTINGS_RECEIVED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
byUri: newByUri,
|
||||
daemonSettings: action.data.settings
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
|
@ -31,27 +31,10 @@ export const selectCurrentParams = createSelector(
|
|||
}
|
||||
)
|
||||
|
||||
export const selectCurrentUri = createSelector(
|
||||
selectCurrentPath,
|
||||
(path) => {
|
||||
if (path.match(/=/)) {
|
||||
return path.split('=')[1]
|
||||
}
|
||||
else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const selectCurrentUriTitle = createSelector(
|
||||
_selectState,
|
||||
(state) => "fix me"
|
||||
)
|
||||
|
||||
export const selectPageTitle = createSelector(
|
||||
selectCurrentPage,
|
||||
selectCurrentUri,
|
||||
(page, uri) => {
|
||||
selectCurrentParams,
|
||||
(page, params) => {
|
||||
switch (page) {
|
||||
case 'search':
|
||||
return 'Search'
|
||||
|
@ -67,7 +50,7 @@ export const selectPageTitle = createSelector(
|
|||
case 'rewards':
|
||||
return page.charAt(0).toUpperCase() + page.slice(1)
|
||||
case 'show':
|
||||
return lbryuri.normalize(uri)
|
||||
return lbryuri.normalize(params.uri)
|
||||
case 'downloaded':
|
||||
return 'Downloads & Purchases'
|
||||
case 'published':
|
||||
|
@ -195,11 +178,6 @@ export const selectUpgradeDownloadItem = createSelector(
|
|||
(state) => state.downloadItem
|
||||
)
|
||||
|
||||
export const selectSearchTerm = createSelector(
|
||||
_selectState,
|
||||
(state) => state.searchTerm
|
||||
)
|
||||
|
||||
export const selectError = createSelector(
|
||||
_selectState,
|
||||
(state) => state.error
|
||||
|
@ -214,13 +192,3 @@ export const selectObscureNsfw = createSelector(
|
|||
_selectState,
|
||||
(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 {
|
||||
selectDaemonReady,
|
||||
selectCurrentPage,
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
|
||||
const _selectState = state => state.availability
|
||||
|
@ -14,29 +13,24 @@ export const selectAvailabilityByUri = createSelector(
|
|||
(state) => state.byUri || {}
|
||||
)
|
||||
|
||||
const selectAvailabilityForUri = (state, props) => {
|
||||
return selectAvailabilityByUri(state)[props.uri]
|
||||
}
|
||||
|
||||
export const makeSelectIsAvailableForUri = () => {
|
||||
return createSelector(
|
||||
selectAvailabilityForUri,
|
||||
(availability) => availability === undefined ? undefined : availability > 0
|
||||
)
|
||||
}
|
||||
|
||||
export const selectFetchingAvailability = createSelector(
|
||||
_selectState,
|
||||
(state) => state.fetching || {}
|
||||
)
|
||||
|
||||
export const selectFetchingAvailabilityByUri = createSelector(
|
||||
selectFetchingAvailability,
|
||||
(fetching) => fetching.byUri || {}
|
||||
)
|
||||
|
||||
const selectAvailabilityForUri = (state, props) => {
|
||||
return selectAvailabilityByUri(state)[props.uri]
|
||||
}
|
||||
|
||||
export const makeSelectAvailabilityForUri = () => {
|
||||
return createSelector(
|
||||
selectAvailabilityForUri,
|
||||
(availability) => availability
|
||||
)
|
||||
}
|
||||
|
||||
const selectFetchingAvailabilityForUri = (state, props) => {
|
||||
return selectFetchingAvailabilityByUri(state)[props.uri]
|
||||
return selectFetchingAvailability(state)[props.uri]
|
||||
}
|
||||
|
||||
export const makeSelectFetchingAvailabilityForUri = () => {
|
||||
|
@ -45,15 +39,3 @@ export const makeSelectFetchingAvailabilityForUri = () => {
|
|||
(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,
|
||||
} from 'reselect'
|
||||
import lbryuri from 'lbryuri'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
|
||||
export const _selectState = state => state.claims || {}
|
||||
|
||||
export const selectClaimsByUri = createSelector(
|
||||
_selectState,
|
||||
(state) => state.byUri || {}
|
||||
(state) => state.claimsByUri || {}
|
||||
)
|
||||
|
||||
export const selectCurrentUriClaim = createSelector(
|
||||
selectCurrentUri,
|
||||
selectClaimsByUri,
|
||||
(uri, byUri) => byUri[uri]
|
||||
)
|
||||
|
||||
export const selectCurrentUriClaimOutpoint = createSelector(
|
||||
selectCurrentUriClaim,
|
||||
(claim) => {
|
||||
return claim ? `${claim.txid}:${claim.nout}` : null
|
||||
}
|
||||
export const selectAllClaimsByChannel = createSelector(
|
||||
_selectState,
|
||||
(state) => state.claimsByChannel || {}
|
||||
)
|
||||
|
||||
const selectClaimForUri = (state, props) => {
|
||||
|
@ -38,11 +27,22 @@ export const makeSelectClaimForUri = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export const selectClaimsInChannelForUri = (state, props) => {
|
||||
return selectAllClaimsByChannel(state)[props.uri]
|
||||
}
|
||||
|
||||
export const makeSelectClaimsInChannelForUri = () => {
|
||||
return createSelector(
|
||||
selectClaimsInChannelForUri,
|
||||
(claims) => claims
|
||||
)
|
||||
}
|
||||
|
||||
const selectMetadataForUri = (state, props) => {
|
||||
const claim = selectClaimForUri(state, props)
|
||||
const metadata = claim && claim.value && claim.value.stream && claim.value.stream.metadata
|
||||
|
||||
return metadata ? metadata : undefined
|
||||
return metadata ? metadata : (claim === undefined ? undefined : null)
|
||||
}
|
||||
|
||||
export const makeSelectMetadataForUri = () => {
|
||||
|
@ -56,7 +56,7 @@ const selectSourceForUri = (state, props) => {
|
|||
const claim = selectClaimForUri(state, props)
|
||||
const source = claim && claim.value && claim.value.stream && claim.value.stream.source
|
||||
|
||||
return source ? source : undefined
|
||||
return source ? source : (claim === undefined ? undefined : null)
|
||||
}
|
||||
|
||||
export const makeSelectSourceForUri = () => {
|
||||
|
@ -66,26 +66,32 @@ export const makeSelectSourceForUri = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export const selectMyClaims = createSelector(
|
||||
export const makeSelectContentTypeForUri = () => {
|
||||
return createSelector(
|
||||
selectSourceForUri,
|
||||
(source) => source ? source.contentType : source
|
||||
)
|
||||
}
|
||||
|
||||
export const selectClaimListMineIsPending = createSelector(
|
||||
_selectState,
|
||||
(state) => state.mine || {}
|
||||
(state) => state.isClaimListMinePending
|
||||
)
|
||||
|
||||
export const selectMyClaimsById = createSelector(
|
||||
selectMyClaims,
|
||||
(mine) => mine.byId || {}
|
||||
export const selectMyClaims = createSelector(
|
||||
_selectState,
|
||||
(state) => state.myClaims || {}
|
||||
)
|
||||
|
||||
export const selectMyClaimsOutpoints = createSelector(
|
||||
selectMyClaimsById,
|
||||
(byId) => {
|
||||
const outpoints = []
|
||||
Object.keys(byId).forEach(key => {
|
||||
const claim = byId[key]
|
||||
const outpoint = `${claim.txid}:${claim.nout}`
|
||||
outpoints.push(outpoint)
|
||||
})
|
||||
selectMyClaims,
|
||||
(claims) => {
|
||||
if (!claims) {
|
||||
return []
|
||||
}
|
||||
|
||||
return outpoints
|
||||
return Object.values(claims).map((claim) => {
|
||||
return `${claim.txid}:${claim.nout}`
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@ import { createSelector } from 'reselect'
|
|||
import {
|
||||
selectDaemonReady,
|
||||
selectCurrentPage,
|
||||
selectCurrentUri,
|
||||
} from 'selectors/app'
|
||||
|
||||
export const _selectState = state => state.content || {}
|
||||
|
@ -17,54 +16,16 @@ export const selectFetchingFeaturedUris = createSelector(
|
|||
(state) => !!state.fetchingFeaturedContent
|
||||
)
|
||||
|
||||
export const selectFetchingFileInfos = createSelector(
|
||||
_selectState,
|
||||
(state) => state.fetchingFileInfos || {}
|
||||
)
|
||||
|
||||
export const selectFetchingDownloadedContent = createSelector(
|
||||
_selectState,
|
||||
(state) => !!state.fetchingDownloadedContent
|
||||
)
|
||||
|
||||
export const selectDownloadedContent = createSelector(
|
||||
_selectState,
|
||||
(state) => state.downloadedContent || {}
|
||||
)
|
||||
|
||||
export const selectDownloadedContentFileInfos = createSelector(
|
||||
selectDownloadedContent,
|
||||
(downloadedContent) => downloadedContent.fileInfos || []
|
||||
)
|
||||
|
||||
export const selectFetchingPublishedContent = createSelector(
|
||||
_selectState,
|
||||
(state) => !!state.fetchingPublishedContent
|
||||
)
|
||||
|
||||
export const selectPublishedContent = createSelector(
|
||||
_selectState,
|
||||
(state) => state.publishedContent || {}
|
||||
)
|
||||
|
||||
|
||||
export const selectResolvingUris = createSelector(
|
||||
_selectState,
|
||||
(state) => state.resolvingUris || []
|
||||
)
|
||||
|
||||
|
||||
export const selectCurrentUriIsResolving = createSelector(
|
||||
selectCurrentUri,
|
||||
selectResolvingUris,
|
||||
(uri, resolvingUris) => resolvingUris.indexOf(uri) != -1
|
||||
)
|
||||
|
||||
const selectResolvingUri = (state, props) => {
|
||||
return selectResolvingUris(state).indexOf(props.uri) != -1
|
||||
}
|
||||
|
||||
export const makeSelectResolvingUri = () => {
|
||||
export const makeSelectIsResolvingForUri = () => {
|
||||
return createSelector(
|
||||
selectResolvingUri,
|
||||
(resolving) => resolving
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import { createSelector } from 'reselect'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
selectCurrentPage,
|
||||
} from 'selectors/app'
|
||||
|
||||
export const _selectState = state => state.costInfo || {}
|
||||
|
||||
|
@ -11,24 +7,7 @@ export const selectAllCostInfoByUri = createSelector(
|
|||
(state) => state.byUri || {}
|
||||
)
|
||||
|
||||
export const selectCurrentUriCostInfo = createSelector(
|
||||
selectCurrentUri,
|
||||
selectAllCostInfoByUri,
|
||||
(uri, byUri) => byUri[uri] || {}
|
||||
)
|
||||
|
||||
export const selectFetchingCostInfo = createSelector(
|
||||
_selectState,
|
||||
(state) => state.fetching || {}
|
||||
)
|
||||
|
||||
export const selectFetchingCurrentUriCostInfo = createSelector(
|
||||
selectCurrentUri,
|
||||
selectFetchingCostInfo,
|
||||
(uri, byUri) => !!byUri[uri]
|
||||
)
|
||||
|
||||
const selectCostInfoForUri = (state, props) => {
|
||||
export const selectCostInfoForUri = (state, props) => {
|
||||
return selectAllCostInfoByUri(state)[props.uri]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,72 +1,38 @@
|
|||
import lbry from 'lbry'
|
||||
import {
|
||||
createSelector,
|
||||
} from 'reselect'
|
||||
import {
|
||||
selectCurrentUri,
|
||||
selectCurrentPage,
|
||||
} from 'selectors/app'
|
||||
import {
|
||||
selectClaimsByUri,
|
||||
selectClaimListMineIsPending,
|
||||
selectMyClaimsOutpoints,
|
||||
} from 'selectors/claims'
|
||||
|
||||
export const _selectState = state => state.fileInfo || {}
|
||||
|
||||
export const selectAllFileInfoByUri = createSelector(
|
||||
export const selectAllFileInfos = createSelector(
|
||||
_selectState,
|
||||
(state) => state.byUri || {}
|
||||
(state) => state.fileInfos || {}
|
||||
)
|
||||
|
||||
export const selectCurrentUriRawFileInfo = createSelector(
|
||||
selectCurrentUri,
|
||||
selectAllFileInfoByUri,
|
||||
(uri, byUri) => byUri[uri]
|
||||
)
|
||||
|
||||
export const selectCurrentUriFileInfo = createSelector(
|
||||
selectCurrentUriRawFileInfo,
|
||||
(fileInfo) => fileInfo
|
||||
)
|
||||
|
||||
export const selectFetchingFileInfo = createSelector(
|
||||
export const selectFileListIsPending = createSelector(
|
||||
_selectState,
|
||||
(state) => state.fetching || {}
|
||||
(state) => state.isFileListPending
|
||||
)
|
||||
|
||||
export const selectFetchingCurrentUriFileInfo = createSelector(
|
||||
selectCurrentUri,
|
||||
selectFetchingFileInfo,
|
||||
(uri, byUri) => !!byUri[uri]
|
||||
export const selectFileListDownloadedOrPublishedIsPending = createSelector(
|
||||
selectFileListIsPending,
|
||||
selectClaimListMineIsPending,
|
||||
(isFileListPending, isClaimListMinePending) => isFileListPending || isClaimListMinePending
|
||||
)
|
||||
|
||||
export const selectDownloading = createSelector(
|
||||
_selectState,
|
||||
(state) => state.downloading || {}
|
||||
)
|
||||
export const selectFileInfoForUri = (state, props) => {
|
||||
const claims = selectClaimsByUri(state),
|
||||
claim = claims[props.uri],
|
||||
fileInfos = selectAllFileInfos(state),
|
||||
outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined
|
||||
|
||||
export const selectDownloadingByUri = createSelector(
|
||||
selectDownloading,
|
||||
(downloading) => downloading.byUri || {}
|
||||
)
|
||||
|
||||
export const selectDownloadingCurrentUri = createSelector(
|
||||
selectCurrentUri,
|
||||
selectDownloadingByUri,
|
||||
(uri, byUri) => !!byUri[uri]
|
||||
)
|
||||
|
||||
export const selectCurrentUriIsDownloaded = createSelector(
|
||||
selectCurrentUriFileInfo,
|
||||
(fileInfo) => {
|
||||
if (!fileInfo) return false
|
||||
if (!fileInfo.completed) return false
|
||||
if (!fileInfo.written_bytes > 0) return false
|
||||
|
||||
return true
|
||||
}
|
||||
)
|
||||
|
||||
const selectFileInfoForUri = (state, props) => {
|
||||
return selectAllFileInfoByUri(state)[props.uri]
|
||||
return outpoint && fileInfos ? fileInfos[outpoint] : undefined
|
||||
}
|
||||
|
||||
export const makeSelectFileInfoForUri = () => {
|
||||
|
@ -76,8 +42,13 @@ export const makeSelectFileInfoForUri = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export const selectUrisDownloading = createSelector(
|
||||
_selectState,
|
||||
(state) => state.urisDownloading || {}
|
||||
)
|
||||
|
||||
const selectDownloadingForUri = (state, props) => {
|
||||
const byUri = selectDownloadingByUri(state)
|
||||
const byUri = selectUrisDownloading(state)
|
||||
return byUri[props.uri]
|
||||
}
|
||||
|
||||
|
@ -88,31 +59,13 @@ export const makeSelectDownloadingForUri = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export const selectLoading = createSelector(
|
||||
export const selectUrisLoading = createSelector(
|
||||
_selectState,
|
||||
(state) => state.loading || {}
|
||||
)
|
||||
|
||||
export const selectLoadingByUri = createSelector(
|
||||
selectLoading,
|
||||
(loading) => loading.byUri || {}
|
||||
)
|
||||
|
||||
export const selectLoadingCurrentUri = createSelector(
|
||||
selectLoadingByUri,
|
||||
selectCurrentUri,
|
||||
(byUri, uri) => !!byUri[uri]
|
||||
)
|
||||
|
||||
// TODO make this smarter so it doesn't start playing and immediately freeze
|
||||
// while downloading more.
|
||||
export const selectCurrentUriFileReadyToPlay = createSelector(
|
||||
selectCurrentUriFileInfo,
|
||||
(fileInfo) => (fileInfo || {}).written_bytes > 0
|
||||
(state) => state.urisLoading || {}
|
||||
)
|
||||
|
||||
const selectLoadingForUri = (state, props) => {
|
||||
const byUri = selectLoadingByUri(state)
|
||||
const byUri = selectUrisLoading(state)
|
||||
return byUri[props.uri]
|
||||
}
|
||||
|
||||
|
@ -123,36 +76,38 @@ export const makeSelectLoadingForUri = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export const selectDownloadedFileInfo = createSelector(
|
||||
selectAllFileInfoByUri,
|
||||
(byUri) => {
|
||||
export const selectFileInfosDownloaded = createSelector(
|
||||
selectAllFileInfos,
|
||||
selectMyClaimsOutpoints,
|
||||
(fileInfos, myClaimOutpoints) => {
|
||||
const fileInfoList = []
|
||||
Object.keys(byUri).forEach(key => {
|
||||
const fileInfo = byUri[key]
|
||||
|
||||
if (fileInfo.completed || fileInfo.written_bytes) {
|
||||
Object.values(fileInfos).forEach(fileInfo => {
|
||||
if (fileInfo && myClaimOutpoints.indexOf(fileInfo.outpoint) === -1 && (fileInfo.completed || fileInfo.written_bytes)) {
|
||||
fileInfoList.push(fileInfo)
|
||||
}
|
||||
})
|
||||
|
||||
return fileInfoList
|
||||
}
|
||||
)
|
||||
|
||||
export const selectPublishedFileInfo = createSelector(
|
||||
selectAllFileInfoByUri,
|
||||
selectMyClaimsOutpoints,
|
||||
(byUri, outpoints) => {
|
||||
const fileInfos = []
|
||||
outpoints.forEach(outpoint => {
|
||||
Object.keys(byUri).forEach(key => {
|
||||
const fileInfo = byUri[key]
|
||||
if (fileInfo.outpoint == outpoint) {
|
||||
fileInfos.push(fileInfo)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return fileInfos
|
||||
export const selectFileInfosPendingPublish = createSelector(
|
||||
_selectState,
|
||||
(state) => {
|
||||
return lbry.getPendingPublishes()
|
||||
}
|
||||
)
|
||||
|
||||
export const selectFileInfosPublished = createSelector(
|
||||
selectAllFileInfos,
|
||||
selectFileInfosPendingPublish,
|
||||
selectMyClaimsOutpoints,
|
||||
(allFileInfos, pendingFileInfos, outpoints) => {
|
||||
const fileInfos = []
|
||||
outpoints.forEach(outpoint => {
|
||||
if (allFileInfos[outpoint]) {
|
||||
fileInfos.push(allFileInfos[outpoint])
|
||||
}
|
||||
})
|
||||
return [...fileInfos, ...pendingFileInfos]
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@ import { createSelector } from 'reselect'
|
|||
import {
|
||||
selectPageTitle,
|
||||
selectCurrentPage,
|
||||
selectCurrentUri
|
||||
} from 'selectors/app'
|
||||
|
||||
export const _selectState = state => state.search || {}
|
||||
|
@ -43,8 +42,7 @@ export const selectWunderBarAddress = createSelector(
|
|||
|
||||
export const selectWunderBarIcon = createSelector(
|
||||
selectCurrentPage,
|
||||
selectCurrentUri,
|
||||
(page, uri) => {
|
||||
(page) => {
|
||||
switch (page) {
|
||||
case 'search':
|
||||
return 'icon-search'
|
||||
|
|
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'
|
||||
import appReducer from 'reducers/app';
|
||||
import availabilityReducer from 'reducers/availability'
|
||||
import certificatesReducer from 'reducers/certificates'
|
||||
import claimsReducer from 'reducers/claims'
|
||||
import contentReducer from 'reducers/content';
|
||||
import costInfoReducer from 'reducers/cost_info'
|
||||
import fileInfoReducer from 'reducers/file_info'
|
||||
import rewardsReducer from 'reducers/rewards'
|
||||
import searchReducer from 'reducers/search'
|
||||
import settingsReducer from 'reducers/settings'
|
||||
import walletReducer from 'reducers/wallet'
|
||||
|
||||
function isFunction(object) {
|
||||
|
@ -49,13 +49,13 @@ function enableBatching(reducer) {
|
|||
const reducers = redux.combineReducers({
|
||||
app: appReducer,
|
||||
availability: availabilityReducer,
|
||||
certificates: certificatesReducer,
|
||||
claims: claimsReducer,
|
||||
fileInfo: fileInfoReducer,
|
||||
content: contentReducer,
|
||||
costInfo: costInfoReducer,
|
||||
rewards: rewardsReducer,
|
||||
search: searchReducer,
|
||||
settings: settingsReducer,
|
||||
wallet: walletReducer,
|
||||
});
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
"babel-cli": "^6.11.4",
|
||||
"babel-preset-es2015": "^6.13.2",
|
||||
"babel-preset-react": "^6.11.1",
|
||||
"mediaelement": "^2.23.4",
|
||||
"node-sass": "^3.8.0",
|
||||
"plyr": "^2.0.12",
|
||||
"rc-progress": "^2.0.6",
|
||||
|
|
|
@ -143,6 +143,14 @@ p
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
/*should be redone/moved*/
|
||||
.file-list__header {
|
||||
.busy-indicator {
|
||||
float: left;
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-section {
|
||||
display: block;
|
||||
margin-bottom: $spacing-vertical * 2/3;
|
||||
|
|
|
@ -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 "_icons";
|
||||
@import "_mediaelement";
|
||||
@import "_gui";
|
||||
@import "component/_table";
|
||||
@import "component/_button.scss";
|
||||
|
@ -20,6 +19,5 @@
|
|||
@import "component/_snack-bar.scss";
|
||||
@import "component/_video.scss";
|
||||
@import "page/_developer.scss";
|
||||
@import "page/_watch.scss";
|
||||
@import "page/_reward.scss";
|
||||
@import "page/_show.scss";
|
||||
|
|
|
@ -3,6 +3,9 @@ video {
|
|||
box-sizing: border-box;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
background-size: contain;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.video {
|
||||
|
@ -15,6 +18,7 @@ video {
|
|||
max-width: $width-page-constrained;
|
||||
max-height: $height-video-embedded;
|
||||
height: $height-video-embedded;
|
||||
position: relative; /*for .plyr below*/
|
||||
video {
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -24,6 +28,10 @@ video {
|
|||
&.video--active {
|
||||
/*background: none;*/
|
||||
}
|
||||
.plyr {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.video__cover {
|
||||
|
|
|
@ -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…
Add table
Reference in a new issue