Merge pull request #1 from lbryio/master #651
137 changed files with 2519 additions and 1819 deletions
|
@ -1,5 +1,5 @@
|
||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.15.1
|
current_version = 0.16.0
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((?P<release>[a-z]+)(?P<candidate>\d+))?
|
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((?P<release>[a-z]+)(?P<candidate>\d+))?
|
||||||
|
|
23
CHANGELOG.md
23
CHANGELOG.md
|
@ -27,6 +27,29 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|
||||||
|
## [0.16.0] - 2017-09-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
* Added a tipping button to send LBRY Credits to a creator.
|
||||||
|
* Added an edit button on published content. Significantly improved UX for editing claims.
|
||||||
|
* Added theme settings option and new Dark theme.
|
||||||
|
* Significantly more detail is shown about past transactions and new filtering options for transactions.
|
||||||
|
* File pages now show the time of a publish.
|
||||||
|
* The "auth token" displayable on Help offers security warning
|
||||||
|
* Added a new component for rendering dates and times. This component can render the date and time of a block height, as well.
|
||||||
|
* Added a `Form` component, to further progress towards form sanity.
|
||||||
|
* Added `gnome-keyring` dependency to .deb
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* CSS significantly refactored to support CSS vars (and consequently easy theming).
|
||||||
|
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* URLs on cards no longer wrap and show an ellipsis if longer than one line
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.15.1] - 2017-09-08
|
## [0.15.1] - 2017-09-08
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
48
README.md
48
README.md
|
@ -46,10 +46,52 @@ to create distributable packages, which is run by calling:
|
||||||
|
|
||||||
`node_modules/.bin/build -p never`
|
`node_modules/.bin/build -p never`
|
||||||
|
|
||||||
### Development on Windows
|
## Development on Windows
|
||||||
|
|
||||||
This project has currently only been worked on in Linux and macOS. If you are on Windows, you can
|
### Windows Dependency
|
||||||
checkout out the build steps in [appveyor.yml](https://github.com/lbryio/lbry-app/blob/master/.appveyor.yml) and probably figure out something from there.
|
|
||||||
|
1. Download and install `npm` and `node` from <a href="https://nodejs.org/en/download/current/">nodejs.org<a>
|
||||||
|
2. Download and install `python 2.7` from <a href="https://www.python.org/downloads/windows/">python.org</a>
|
||||||
|
3. Download and Install `Microsoft Visual C++ Compiler for Python 2.7` from <a href="https://www.microsoft.com/en-us/download/confirmation.aspx?id=44266">Microsoft<a>
|
||||||
|
4. Download and install `.NET Framework 2.0 Software Development Kit (SDK) (x64)` from <a href="https://www.microsoft.com/en-gb/download/details.aspx?id=15354">Microsoft<a>
|
||||||
|
|
||||||
|
### One-time Setup
|
||||||
|
1. Open command prompt in the root of the project and run the following;
|
||||||
|
```
|
||||||
|
python -m pip install -r build\requirements.txt
|
||||||
|
python build\set_version.py
|
||||||
|
npm install -g yarn
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
2. Change directory to `app` and run the following;
|
||||||
|
```
|
||||||
|
yarn install
|
||||||
|
node_modules\.bin\electron-rebuild
|
||||||
|
node_modules\.bin\electron-rebuild
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
3. Change directory to `ui` and run the following
|
||||||
|
```
|
||||||
|
yarn install
|
||||||
|
npm rebuild node-sass
|
||||||
|
node node_modules\node-sass\bin\node-sass --output dist\css --sourcemap=none scss\
|
||||||
|
node_modules\.bin\webpack --config webpack.dev.config.js
|
||||||
|
xcopy dist ..\app\dist
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
4. Download the lbry daemon and cli binaries and place them in `app\dist\`
|
||||||
|
|
||||||
|
### Building lbry-app
|
||||||
|
1. run `node_modules\.bin\build -p never` from the root of the project.
|
||||||
|
|
||||||
|
### Running the electron app
|
||||||
|
1. Run `./node_modules/.bin/electron app`
|
||||||
|
|
||||||
|
### Ongoing Development
|
||||||
|
1. `cd ui`
|
||||||
|
2. `watch.bat`
|
||||||
|
|
||||||
|
This will set up a monitor that will automatically compile any changes to JS or CSS folders inside of the `ui` folder. This allows you to make changes and see them immediately by reloading the app.
|
||||||
|
|
||||||
## Internationalization
|
## Internationalization
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "LBRY",
|
"name": "LBRY",
|
||||||
"version": "0.15.1",
|
"version": "0.16.0",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
|
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
"electron-rebuild": "^1.5.11"
|
"electron-rebuild": "^1.5.11"
|
||||||
},
|
},
|
||||||
"lbrySettings": {
|
"lbrySettings": {
|
||||||
"lbrynetDaemonVersion": "0.15.2",
|
"lbrynetDaemonVersion": "0.16.1",
|
||||||
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-daemon-vDAEMONVER-OSNAME.zip"
|
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-daemon-vDAEMONVER-OSNAME.zip"
|
||||||
},
|
},
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
|
1
build.sh
1
build.sh
|
@ -1,5 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# this is here because teamcity runs /build.sh to build the project
|
# this is here because teamcity runs /build.sh to build the project
|
||||||
set -euxo pipefail
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
$DIR/build/build.sh
|
$DIR/build/build.sh
|
|
@ -1,7 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
set -x
|
|
||||||
|
|
||||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"
|
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
|
@ -10,27 +9,33 @@ BUILD_DIR="$ROOT/build"
|
||||||
LINUX=false
|
LINUX=false
|
||||||
OSX=false
|
OSX=false
|
||||||
if [ "$(uname)" == "Darwin" ]; then
|
if [ "$(uname)" == "Darwin" ]; then
|
||||||
|
echo -e "\033[0;32mBuilding for OSX\x1b[m"
|
||||||
OSX=true
|
OSX=true
|
||||||
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
|
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
|
||||||
|
echo -e "\033[0;32mBuilding for Linux\x1b[m"
|
||||||
LINUX=true
|
LINUX=true
|
||||||
else
|
else
|
||||||
echo "Platform detection failed"
|
echo -e "\033[1;31mPlatform detection failed\x1b[m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if $OSX; then
|
if $OSX; then
|
||||||
ICON="$BUILD_DIR/icon.icns"
|
ICON="$BUILD_DIR/icon.icns"
|
||||||
else
|
else
|
||||||
ICON="$BUILD_DIR/icons/lbry48.png"
|
ICON="$BUILD_DIR/icons/48x48.png"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
FULL_BUILD="${FULL_BUILD:-false}"
|
FULL_BUILD="${FULL_BUILD:-false}"
|
||||||
if [ -n "${TEAMCITY_VERSION:-}" -o -n "${APPVEYOR:-}" ]; then
|
if [ -n "${TEAMCITY_VERSION:-}" -o -n "${APPVEYOR:-}" ]; then
|
||||||
FULL_BUILD="true"
|
FULL_BUILD="true"
|
||||||
fi
|
fi
|
||||||
|
if [ "$FULL_BUILD" != "true" ]; then
|
||||||
|
echo -e "\033[1;36mDependencies will NOT be installed. Run with 'FULL_BUILD=true' to install dependencies.\x1b[m"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$FULL_BUILD" == "true" ]; then
|
if [ "$FULL_BUILD" == "true" ]; then
|
||||||
# install dependencies
|
# install dependencies
|
||||||
|
echo -e "\033[0;32mInstalling Dependencies\x1b[m"
|
||||||
$BUILD_DIR/prebuild.sh
|
$BUILD_DIR/prebuild.sh
|
||||||
|
|
||||||
VENV="$BUILD_DIR/venv"
|
VENV="$BUILD_DIR/venv"
|
||||||
|
@ -57,7 +62,7 @@ yarn install
|
||||||
############
|
############
|
||||||
# UI #
|
# UI #
|
||||||
############
|
############
|
||||||
|
echo -e "\033[0;32mCompiling UI\x1b[m"
|
||||||
(
|
(
|
||||||
cd "$ROOT/ui"
|
cd "$ROOT/ui"
|
||||||
yarn install
|
yarn install
|
||||||
|
@ -73,7 +78,7 @@ yarn install
|
||||||
####################
|
####################
|
||||||
# daemon and cli #
|
# daemon and cli #
|
||||||
####################
|
####################
|
||||||
|
echo -e "\033[0;32mGrabbing Daemon and CLI\x1b[m"
|
||||||
if $OSX; then
|
if $OSX; then
|
||||||
OSNAME="macos"
|
OSNAME="macos"
|
||||||
else
|
else
|
||||||
|
@ -90,7 +95,7 @@ if [[ ! -f $DAEMON_VER_PATH || ! -f $ROOT/app/dist/lbrynet-daemon || "$(< "$DAEM
|
||||||
rm "$BUILD_DIR/daemon.zip"
|
rm "$BUILD_DIR/daemon.zip"
|
||||||
echo "$DAEMON_VER" > "$DAEMON_VER_PATH"
|
echo "$DAEMON_VER" > "$DAEMON_VER_PATH"
|
||||||
else
|
else
|
||||||
echo "Already have daemon version $DAEMON_VER, skipping download"
|
echo -e "\033[4;31mAlready have daemon version $DAEMON_VER, skipping download\x1b[m"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,7 +104,7 @@ fi
|
||||||
###################
|
###################
|
||||||
# Build the app #
|
# Build the app #
|
||||||
###################
|
###################
|
||||||
|
echo -e '\033[0;32mBuilding Lbry-app\x1b[m'
|
||||||
(
|
(
|
||||||
cd "$ROOT/app"
|
cd "$ROOT/app"
|
||||||
yarn install
|
yarn install
|
||||||
|
@ -133,7 +138,7 @@ if [ "$FULL_BUILD" == "true" ]; then
|
||||||
|
|
||||||
deactivate
|
deactivate
|
||||||
|
|
||||||
echo 'Build and packaging complete.'
|
echo -e '\033[0;32mBuild and packaging complete.\x1b[m'
|
||||||
else
|
else
|
||||||
echo 'Build complete. Run `./node_modules/.bin/electron app` to launch the app'
|
echo -e 'Build complete. Run \033[1;31m./node_modules/.bin/electron app\x1b[m to launch the app'
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -48,6 +48,9 @@
|
||||||
"Exec": "/opt/LBRY/lbry %U"
|
"Exec": "/opt/LBRY/lbry %U"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deb": {
|
||||||
|
"depends": ["gconf2", "gconf-service", "libnotify4", "libappindicator1", "libxtst6", "libnss3", "libsecret-1-0"]
|
||||||
|
},
|
||||||
"win": {
|
"win": {
|
||||||
"target": "nsis"
|
"target": "nsis"
|
||||||
},
|
},
|
||||||
|
|
1
ui/dist/quit.html
vendored
1
ui/dist/quit.html
vendored
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
|
||||||
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
|
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
|
||||||
<link href="./js/mediaelement/mediaelementplayer.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link rel="icon" type="image/png" href="./img/fav/favicon-32x32.png" sizes="32x32">
|
<link rel="icon" type="image/png" href="./img/fav/favicon-32x32.png" sizes="32x32">
|
||||||
<link rel="icon" type="image/png" href="./img/fav/favicon-194x194.png" sizes="194x194">
|
<link rel="icon" type="image/png" href="./img/fav/favicon-194x194.png" sizes="194x194">
|
||||||
<link rel="icon" type="image/png" href="./img/fav/favicon-96x96.png" sizes="96x96">
|
<link rel="icon" type="image/png" href="./img/fav/favicon-96x96.png" sizes="96x96">
|
||||||
|
|
46
ui/dist/themes/dark.css
vendored
Normal file
46
ui/dist/themes/dark.css
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
:root {
|
||||||
|
/* Colors */
|
||||||
|
--color-primary: #009688;
|
||||||
|
--color-canvas: #0f1517;
|
||||||
|
--color-bg: #1a2327;
|
||||||
|
--color-bg-alt: #314048;
|
||||||
|
--color-help: #AAA;
|
||||||
|
--color-error: #a94442;
|
||||||
|
--color-load-screen-text: #FFF;
|
||||||
|
--color-money: var(--color-primary);
|
||||||
|
--color-meta-light: #757575;
|
||||||
|
--color-dark-overlay: rgba(15, 21, 23, 0.85);
|
||||||
|
|
||||||
|
/* Text */
|
||||||
|
--text-color: #FFF;
|
||||||
|
--text-selection-bg: rgba(0,150,136, 0.95);
|
||||||
|
|
||||||
|
/* Input */
|
||||||
|
--input-bg: transparent;
|
||||||
|
--input-active-bg: rgba(0,0,0, 0.5);
|
||||||
|
--input-border-color: rgba(255,255,255, 0.25);
|
||||||
|
|
||||||
|
/* Search */
|
||||||
|
--search-bg: rgba(0,0,0, 0.45);
|
||||||
|
--search-color: #757575;
|
||||||
|
--search-active-bg: rgba(0,0,0, 0.75);
|
||||||
|
--search-border: 1px solid rgba(0,0,0, 0.25);
|
||||||
|
|
||||||
|
/* Tab */
|
||||||
|
--tab-color: #757575;
|
||||||
|
--tab-active-color: #CCC;
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
--header-color: #CCC;
|
||||||
|
--header-active-color: #FFF;
|
||||||
|
--header-button-bg: transparent;
|
||||||
|
|
||||||
|
/* Table */
|
||||||
|
--table-border: 0;
|
||||||
|
--table-item-even: var(--color-bg-alt);
|
||||||
|
--table-item-odd: transparent;
|
||||||
|
|
||||||
|
/* Modla */
|
||||||
|
--modal-overlay-bg: var(--color-dark-overlay);
|
||||||
|
--modal-border: 1px solid rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
4
ui/dist/themes/light.css
vendored
Normal file
4
ui/dist/themes/light.css
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
:root {
|
||||||
|
/* Colors */
|
||||||
|
--color-primary: #155B4A;
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
|
import * as settings from "constants/settings";
|
||||||
import lbry from "lbry";
|
import lbry from "lbry";
|
||||||
import {
|
import {
|
||||||
selectUpdateUrl,
|
selectUpdateUrl,
|
||||||
|
@ -8,7 +9,7 @@ import {
|
||||||
} from "selectors/app";
|
} from "selectors/app";
|
||||||
import { doFetchDaemonSettings } from "actions/settings";
|
import { doFetchDaemonSettings } from "actions/settings";
|
||||||
import { doAuthenticate } from "actions/user";
|
import { doAuthenticate } from "actions/user";
|
||||||
import { doFileList } from "actions/file_info";
|
import { doFetchFileInfosAndPublishedClaims } from "actions/file_info";
|
||||||
|
|
||||||
const { remote, ipcRenderer, shell } = require("electron");
|
const { remote, ipcRenderer, shell } = require("electron");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
@ -16,11 +17,12 @@ const { download } = remote.require("electron-dl");
|
||||||
const fs = remote.require("fs");
|
const fs = remote.require("fs");
|
||||||
const { lbrySettings: config } = require("../../../app/package.json");
|
const { lbrySettings: config } = require("../../../app/package.json");
|
||||||
|
|
||||||
export function doOpenModal(modal) {
|
export function doOpenModal(modal, modalProps = {}) {
|
||||||
return {
|
return {
|
||||||
type: types.OPEN_MODAL,
|
type: types.OPEN_MODAL,
|
||||||
data: {
|
data: {
|
||||||
modal,
|
modal,
|
||||||
|
modalProps,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -165,7 +167,7 @@ export function doAlertError(errorList) {
|
||||||
type: types.OPEN_MODAL,
|
type: types.OPEN_MODAL,
|
||||||
data: {
|
data: {
|
||||||
modal: "error",
|
modal: "error",
|
||||||
extraContent: errorList,
|
modalProps: { error: errorList },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -176,7 +178,7 @@ export function doDaemonReady() {
|
||||||
dispatch(doAuthenticate());
|
dispatch(doAuthenticate());
|
||||||
dispatch({ type: types.DAEMON_READY });
|
dispatch({ type: types.DAEMON_READY });
|
||||||
dispatch(doFetchDaemonSettings());
|
dispatch(doFetchDaemonSettings());
|
||||||
dispatch(doFileList());
|
dispatch(doFetchFileInfosAndPublishedClaims());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,11 @@ import { selectFetchingAvailability } from "selectors/availability";
|
||||||
|
|
||||||
export function doFetchAvailability(uri) {
|
export function doFetchAvailability(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
/*
|
||||||
|
this is disabled atm - Jeremy
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const alreadyFetching = !!selectFetchingAvailability(state)[uri];
|
const alreadyFetching = !!selectFetchingAvailability(state)[uri];
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,11 @@ import lbryio from "lbryio";
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
import { selectBalance } from "selectors/wallet";
|
import { selectBalance } from "selectors/wallet";
|
||||||
import {
|
import {
|
||||||
selectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
selectDownloadingByOutpoint,
|
selectDownloadingByOutpoint,
|
||||||
} from "selectors/file_info";
|
} from "selectors/file_info";
|
||||||
import { selectResolvingUris } from "selectors/content";
|
import { selectResolvingUris } from "selectors/content";
|
||||||
import { selectCostInfoForUri } from "selectors/cost_info";
|
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
||||||
import { doAlertError, doOpenModal } from "actions/app";
|
import { doAlertError, doOpenModal } from "actions/app";
|
||||||
import { doClaimEligiblePurchaseRewards } from "actions/rewards";
|
import { doClaimEligiblePurchaseRewards } from "actions/rewards";
|
||||||
import { selectBadgeNumber } from "selectors/app";
|
import { selectBadgeNumber } from "selectors/app";
|
||||||
|
@ -299,13 +299,12 @@ export function doLoadVideo(uri) {
|
||||||
data: { uri },
|
data: { uri },
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(doOpenModal("timedOut"));
|
dispatch(doOpenModal(modals.FILE_TIMEOUT, { uri }));
|
||||||
} else {
|
} else {
|
||||||
dispatch(doDownloadFile(uri, streamInfo));
|
dispatch(doDownloadFile(uri, streamInfo));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log(error);
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.LOADING_VIDEO_FAILED,
|
type: types.LOADING_VIDEO_FAILED,
|
||||||
data: { uri },
|
data: { uri },
|
||||||
|
@ -315,46 +314,37 @@ export function doLoadVideo(uri) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doPurchaseUri(uri, purchaseModalName) {
|
export function doPurchaseUri(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const balance = selectBalance(state);
|
const balance = selectBalance(state);
|
||||||
const fileInfo = selectFileInfoForUri(state, { uri });
|
const fileInfo = makeSelectFileInfoForUri(uri)(state);
|
||||||
const downloadingByOutpoint = selectDownloadingByOutpoint(state);
|
const downloadingByOutpoint = selectDownloadingByOutpoint(state);
|
||||||
const alreadyDownloading =
|
const alreadyDownloading =
|
||||||
fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
|
fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
|
||||||
|
const costInfo = makeSelectCostInfoForUri(uri)(state);
|
||||||
// we already fully downloaded the file.
|
|
||||||
if (fileInfo && fileInfo.completed) {
|
|
||||||
// If written_bytes is false that means the user has deleted/moved the
|
|
||||||
// file manually on their file system, so we need to dispatch a
|
|
||||||
// doLoadVideo action to reconstruct the file from the blobs
|
|
||||||
if (!fileInfo.written_bytes) dispatch(doLoadVideo(uri));
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are already downloading the file
|
|
||||||
if (alreadyDownloading) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
const costInfo = selectCostInfoForUri(state, { uri });
|
|
||||||
const { cost } = costInfo;
|
const { cost } = costInfo;
|
||||||
|
|
||||||
// the file is free or we have partially downloaded it
|
if (
|
||||||
if (cost === 0 || (fileInfo && fileInfo.download_directory)) {
|
alreadyDownloading ||
|
||||||
dispatch(doLoadVideo(uri));
|
(fileInfo && fileInfo.completed && fileInfo.written_bytes > 0)
|
||||||
return Promise.resolve();
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we already fully downloaded the file.
|
||||||
|
if (
|
||||||
|
cost === 0 ||
|
||||||
|
(fileInfo && (fileInfo.completed || fileInfo.download_directory))
|
||||||
|
) {
|
||||||
|
return dispatch(doLoadVideo(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cost > balance) {
|
if (cost > balance) {
|
||||||
dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS));
|
return dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS));
|
||||||
} else {
|
|
||||||
dispatch(doOpenModal(purchaseModalName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
return dispatch(doOpenModal(modals.AFFIRM_PURCHASE, { uri }));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +355,7 @@ export function doFetchClaimsByChannel(uri, page) {
|
||||||
data: { uri, page },
|
data: { uri, page },
|
||||||
});
|
});
|
||||||
|
|
||||||
lbry.claim_list_by_channel({ uri, page }).then(result => {
|
lbry.claim_list_by_channel({ uri, page: page || 1 }).then(result => {
|
||||||
const claimResult = result[uri],
|
const claimResult = result[uri],
|
||||||
claims = claimResult ? claimResult.claims_in_channel : [],
|
claims = claimResult ? claimResult.claims_in_channel : [],
|
||||||
currentPage = claimResult ? claimResult.returned_page : undefined;
|
currentPage = claimResult ? claimResult.returned_page : undefined;
|
||||||
|
@ -421,6 +411,22 @@ export function doFetchClaimListMine() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doPlayUri(uri) {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
dispatch(doSetPlayingUri(uri));
|
||||||
|
dispatch(doPurchaseUri(uri));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSetPlayingUri(uri) {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
dispatch({
|
||||||
|
type: types.SET_PLAYING_URI,
|
||||||
|
data: { uri },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function doFetchChannelListMine() {
|
export function doFetchChannelListMine() {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|
|
@ -33,26 +33,53 @@ export function doFetchCostInfoForUri(uri) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isGenerous && claim) {
|
/**
|
||||||
let cost;
|
* "Generous" check below is disabled. We're no longer attempting to include or estimate data fees regardless of settings.
|
||||||
const fee = claim.value &&
|
*
|
||||||
claim.value.stream &&
|
* This should be modified when lbry.stream_cost_estimate is reliable and performant.
|
||||||
claim.value.stream.metadata
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
lbry.stream_cost_estimate({ uri }).then(cost => {
|
||||||
|
cacheAndResolve(cost);
|
||||||
|
}, reject);
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fee = claim.value && claim.value.stream && claim.value.stream.metadata
|
||||||
? claim.value.stream.metadata.fee
|
? claim.value.stream.metadata.fee
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (fee === undefined) {
|
if (fee === undefined) {
|
||||||
resolve({ cost: 0, includesData: true });
|
resolve({ cost: 0, includesData: true });
|
||||||
} else if (fee.currency == "LBC") {
|
} else if (fee.currency == "LBC") {
|
||||||
resolve({ cost: fee.amount, includesData: true });
|
resolve({ cost: fee.amount, includesData: true });
|
||||||
} else {
|
} else {
|
||||||
begin();
|
// begin();
|
||||||
lbryio.getExchangeRates().then(({ lbc_usd }) => {
|
lbryio.getExchangeRates().then(({ lbc_usd }) => {
|
||||||
resolve({ cost: fee.amount / lbc_usd, includesData: true });
|
resolve({ cost: fee.amount / lbc_usd, includesData: true });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
begin();
|
// if (isGenerous && claim) {
|
||||||
lbry.getCostInfo(uri).then(resolve);
|
// let cost;
|
||||||
}
|
// const fee = claim.value &&
|
||||||
|
// claim.value.stream &&
|
||||||
|
// claim.value.stream.metadata
|
||||||
|
// ? claim.value.stream.metadata.fee
|
||||||
|
// : undefined;
|
||||||
|
// 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);
|
||||||
|
// }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
selectTotalDownloadProgress,
|
selectTotalDownloadProgress,
|
||||||
} from "selectors/file_info";
|
} from "selectors/file_info";
|
||||||
import { doCloseModal } from "actions/app";
|
import { doCloseModal } from "actions/app";
|
||||||
import { doHistoryBack } from "actions/navigation";
|
import { doNavigate, doHistoryBack } from "actions/navigation";
|
||||||
import setProgressBar from "util/setProgressBar";
|
import setProgressBar from "util/setProgressBar";
|
||||||
import batchActions from "util/batchActions";
|
import batchActions from "util/batchActions";
|
||||||
|
|
||||||
|
@ -71,18 +71,18 @@ export function doFileList() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doOpenFileInShell(fileInfo) {
|
export function doOpenFileInShell(path) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const success = shell.openItem(fileInfo.download_path);
|
const success = shell.openItem(path);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
dispatch(doOpenFileInFolder(fileInfo));
|
dispatch(doOpenFileInFolder(path));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doOpenFileInFolder(fileInfo) {
|
export function doOpenFileInFolder(path) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
shell.showItemInFolder(fileInfo.download_path);
|
shell.showItemInFolder(path);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
import {
|
import {
|
||||||
|
computePageFromPath,
|
||||||
selectPageTitle,
|
selectPageTitle,
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentParams,
|
selectCurrentParams,
|
||||||
|
@ -20,9 +21,17 @@ export function doNavigate(path, params = {}, options = {}) {
|
||||||
url += "?" + toQueryString(params);
|
url += "?" + toQueryString(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(doChangePath(url, options));
|
const state = getState(),
|
||||||
|
currentPage = selectCurrentPage(state),
|
||||||
|
nextPage = computePageFromPath(path),
|
||||||
|
scrollY = options.scrollY;
|
||||||
|
|
||||||
const pageTitle = selectPageTitle(getState()) + " - LBRY";
|
if (currentPage != nextPage) {
|
||||||
|
//I wasn't seeing it scroll to the proper position without this -- possibly because the page isn't fully rendered? Not sure - Jeremy
|
||||||
|
setTimeout(() => {
|
||||||
|
window.scrollTo(0, scrollY ? scrollY : 0);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.HISTORY_NAVIGATE,
|
type: types.HISTORY_NAVIGATE,
|
||||||
|
@ -45,31 +54,6 @@ export function doAuthNavigate(pathAfterAuth = null, params = {}) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doChangePath(path, options = {}) {
|
|
||||||
return function(dispatch, getState) {
|
|
||||||
dispatch({
|
|
||||||
type: types.CHANGE_PATH,
|
|
||||||
data: {
|
|
||||||
path,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const state = getState();
|
|
||||||
const scrollY = options.scrollY;
|
|
||||||
|
|
||||||
//I wasn't seeing it scroll to the proper position without this -- possibly because the page isn't fully rendered? Not sure - Jeremy
|
|
||||||
setTimeout(() => {
|
|
||||||
window.scrollTo(0, scrollY ? scrollY : 0);
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
const currentPage = selectCurrentPage(state);
|
|
||||||
if (currentPage === "search") {
|
|
||||||
const params = selectCurrentParams(state);
|
|
||||||
dispatch(doSearch(params.query));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function doHistoryTraverse(dispatch, state, modifier) {
|
export function doHistoryTraverse(dispatch, state, modifier) {
|
||||||
const stack = selectHistoryStack(state),
|
const stack = selectHistoryStack(state),
|
||||||
index = selectHistoryIndex(state) + modifier;
|
index = selectHistoryIndex(state) + modifier;
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
import * as settings from "constants/settings";
|
import * as settings from "constants/settings";
|
||||||
|
import { doAlertError } from "actions/app";
|
||||||
import batchActions from "util/batchActions";
|
import batchActions from "util/batchActions";
|
||||||
|
|
||||||
import lbry from "lbry";
|
import lbry from "lbry";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import http from "http";
|
import http from "http";
|
||||||
|
|
||||||
|
const { remote } = require("electron");
|
||||||
|
const { extname } = require("path");
|
||||||
|
const { readdir } = remote.require("fs");
|
||||||
|
|
||||||
export function doFetchDaemonSettings() {
|
export function doFetchDaemonSettings() {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
lbry.settings_get().then(settings => {
|
lbry.settings_get().then(settings => {
|
||||||
|
@ -46,6 +52,27 @@ export function doSetClientSetting(key, value) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doGetThemes() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const dir = `${remote.app.getAppPath()}/dist/themes`;
|
||||||
|
|
||||||
|
readdir(dir, (error, files) => {
|
||||||
|
if (!error) {
|
||||||
|
dispatch(
|
||||||
|
doSetClientSetting(
|
||||||
|
settings.THEMES,
|
||||||
|
files
|
||||||
|
.filter(file => extname(file) === ".css")
|
||||||
|
.map(file => file.replace(".css", ""))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
dispatch(doAlertError(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function doDownloadLanguage(langFile) {
|
export function doDownloadLanguage(langFile) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const destinationPath = app.i18n.directory + "/" + langFile;
|
const destinationPath = app.i18n.directory + "/" + langFile;
|
||||||
|
@ -137,7 +164,7 @@ export function doDownloadLanguages() {
|
||||||
|
|
||||||
export function doChangeLanguage(language) {
|
export function doChangeLanguage(language) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
lbry.setClientSetting(settings.LANGUAGE, language);
|
dispatch(doSetClientSetting(settings.LANGUAGE, language));
|
||||||
app.i18n.setLocale(language);
|
app.i18n.setLocale(language);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ import {
|
||||||
selectDraftTransactionAmount,
|
selectDraftTransactionAmount,
|
||||||
selectBalance,
|
selectBalance,
|
||||||
} from "selectors/wallet";
|
} from "selectors/wallet";
|
||||||
import { doOpenModal } from "actions/app";
|
import { doOpenModal, doShowSnackBar } from "actions/app";
|
||||||
|
import { doNavigate } from "actions/navigation";
|
||||||
|
import * as modals from "constants/modal_types";
|
||||||
|
|
||||||
export function doUpdateBalance(balance) {
|
export function doUpdateBalance(balance) {
|
||||||
return {
|
return {
|
||||||
|
@ -22,7 +24,7 @@ export function doFetchTransactions() {
|
||||||
type: types.FETCH_TRANSACTIONS_STARTED,
|
type: types.FETCH_TRANSACTIONS_STARTED,
|
||||||
});
|
});
|
||||||
|
|
||||||
lbry.transaction_list().then(results => {
|
lbry.transaction_list({ include_tip_info: true }).then(results => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_TRANSACTIONS_COMPLETED,
|
type: types.FETCH_TRANSACTIONS_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
|
@ -83,8 +85,8 @@ export function doSendDraftTransaction() {
|
||||||
const balance = selectBalance(state);
|
const balance = selectBalance(state);
|
||||||
const amount = selectDraftTransactionAmount(state);
|
const amount = selectDraftTransactionAmount(state);
|
||||||
|
|
||||||
if (balance - amount < 1) {
|
if (balance - amount <= 0) {
|
||||||
return dispatch(doOpenModal("insufficientBalance"));
|
return dispatch(doOpenModal(modals.INSUFFICIENT_BALANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -96,13 +98,19 @@ export function doSendDraftTransaction() {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.SEND_TRANSACTION_COMPLETED,
|
type: types.SEND_TRANSACTION_COMPLETED,
|
||||||
});
|
});
|
||||||
dispatch(doOpenModal("transactionSuccessful"));
|
dispatch(
|
||||||
|
doShowSnackBar({
|
||||||
|
message: __(`You sent ${amount} LBC`),
|
||||||
|
linkText: __("History"),
|
||||||
|
linkTarget: __("/wallet"),
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.SEND_TRANSACTION_FAILED,
|
type: types.SEND_TRANSACTION_FAILED,
|
||||||
data: { error: results },
|
data: { error: results },
|
||||||
});
|
});
|
||||||
dispatch(doOpenModal("transactionFailed"));
|
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,7 +119,7 @@ export function doSendDraftTransaction() {
|
||||||
type: types.SEND_TRANSACTION_FAILED,
|
type: types.SEND_TRANSACTION_FAILED,
|
||||||
data: { error: error.message },
|
data: { error: error.message },
|
||||||
});
|
});
|
||||||
dispatch(doOpenModal("transactionFailed"));
|
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
|
||||||
};
|
};
|
||||||
|
|
||||||
lbry
|
lbry
|
||||||
|
@ -136,3 +144,55 @@ export function doSetDraftTransactionAddress(address) {
|
||||||
data: { address },
|
data: { address },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doSendSupport(amount, claim_id, uri) {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState();
|
||||||
|
const balance = selectBalance(state);
|
||||||
|
|
||||||
|
if (balance - amount <= 0) {
|
||||||
|
return dispatch(doOpenModal(modals.INSUFFICIENT_BALANCE));
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: types.SUPPORT_TRANSACTION_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const successCallback = results => {
|
||||||
|
if (results.txid) {
|
||||||
|
dispatch({
|
||||||
|
type: types.SUPPORT_TRANSACTION_COMPLETED,
|
||||||
|
});
|
||||||
|
dispatch(
|
||||||
|
doShowSnackBar({
|
||||||
|
message: __(`You sent ${amount} LBC as support, Mahalo!`),
|
||||||
|
linkText: __("History"),
|
||||||
|
linkTarget: __("/wallet"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
dispatch(doNavigate("/show", { uri }));
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: types.SUPPORT_TRANSACTION_FAILED,
|
||||||
|
data: { error: results.code },
|
||||||
|
});
|
||||||
|
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorCallback = error => {
|
||||||
|
dispatch({
|
||||||
|
type: types.SUPPORT_TRANSACTION_FAILED,
|
||||||
|
data: { error: error.code },
|
||||||
|
});
|
||||||
|
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
|
||||||
|
};
|
||||||
|
|
||||||
|
lbry
|
||||||
|
.wallet_send({
|
||||||
|
claim_id: claim_id,
|
||||||
|
amount: amount,
|
||||||
|
})
|
||||||
|
.then(successCallback, errorCallback);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,14 @@
|
||||||
import store from "store.js";
|
import store from "store.js";
|
||||||
import lbry from "./lbry.js";
|
|
||||||
import { remote } from "electron";
|
import { remote } from "electron";
|
||||||
import * as settings from "constants/settings";
|
|
||||||
|
|
||||||
const env = ENV;
|
const env = ENV;
|
||||||
const config = {
|
const config = {
|
||||||
...require(`./config/${env}`),
|
...require(`./config/${env}`),
|
||||||
};
|
};
|
||||||
const language = lbry.getClientSetting(settings.LANGUAGE)
|
|
||||||
? lbry.getClientSetting(settings.LANGUAGE)
|
|
||||||
: "en";
|
|
||||||
const i18n = require("y18n")({
|
const i18n = require("y18n")({
|
||||||
directory: remote.app.getAppPath() + "/locales",
|
directory: remote.app.getAppPath() + "/locales",
|
||||||
updateFiles: false,
|
updateFiles: false,
|
||||||
locale: language,
|
locale: "en",
|
||||||
});
|
});
|
||||||
const logs = [];
|
const logs = [];
|
||||||
const app = {
|
const app = {
|
||||||
|
|
|
@ -2,10 +2,7 @@ import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { selectPageTitle } from "selectors/navigation";
|
import { selectPageTitle } from "selectors/navigation";
|
||||||
import { selectUser } from "selectors/user";
|
import { selectUser } from "selectors/user";
|
||||||
import {
|
import { doCheckUpgradeAvailable, doAlertError } from "actions/app";
|
||||||
doCheckUpgradeAvailable,
|
|
||||||
doAlertError,
|
|
||||||
} from "actions/app";
|
|
||||||
import { doRecordScroll } from "actions/navigation";
|
import { doRecordScroll } from "actions/navigation";
|
||||||
import { doFetchRewardedContent } from "actions/content";
|
import { doFetchRewardedContent } from "actions/content";
|
||||||
import { doUpdateBalance } from "actions/wallet";
|
import { doUpdateBalance } from "actions/wallet";
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Router from "component/router/index";
|
import Router from "component/router/index";
|
||||||
import Header from "component/header";
|
import Header from "component/header";
|
||||||
|
import Theme from "component/theme";
|
||||||
import ModalRouter from "modal/modalRouter";
|
import ModalRouter from "modal/modalRouter";
|
||||||
import lbry from "lbry";
|
import lbry from "lbry";
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@ class App extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="window">
|
<div id="window">
|
||||||
|
<Theme />
|
||||||
<Header />
|
<Header />
|
||||||
<div id="main-content">
|
<div id="main-content">
|
||||||
<Router />
|
<Router />
|
||||||
|
|
|
@ -70,7 +70,7 @@ export class CreditAmount extends React.PureComponent {
|
||||||
showFree: React.PropTypes.bool,
|
showFree: React.PropTypes.bool,
|
||||||
showFullPrice: React.PropTypes.bool,
|
showFullPrice: React.PropTypes.bool,
|
||||||
showPlus: React.PropTypes.bool,
|
showPlus: React.PropTypes.bool,
|
||||||
look: React.PropTypes.oneOf(["indicator", "plain"]),
|
look: React.PropTypes.oneOf(["indicator", "plain", "fee"]),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -78,7 +78,6 @@ export class CreditAmount extends React.PureComponent {
|
||||||
label: true,
|
label: true,
|
||||||
showFree: false,
|
showFree: false,
|
||||||
look: "indicator",
|
look: "indicator",
|
||||||
showFree: false,
|
|
||||||
showFullPrice: false,
|
showFullPrice: false,
|
||||||
showPlus: false,
|
showPlus: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
class DateTime extends React.PureComponent {
|
class DateTime extends React.PureComponent {
|
||||||
|
static SHOW_DATE = "date";
|
||||||
|
static SHOW_TIME = "time";
|
||||||
|
static SHOW_BOTH = "both";
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.refreshDate(this.props);
|
this.refreshDate(this.props);
|
||||||
}
|
}
|
||||||
|
@ -17,9 +21,20 @@ class DateTime extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { date } = this.props;
|
const { date, formatOptions } = this.props;
|
||||||
|
const show = this.props.show || DateTime.SHOW_BOTH;
|
||||||
|
|
||||||
return <span>{date && date.toLocaleString()}</span>;
|
return (
|
||||||
|
<span>
|
||||||
|
{date &&
|
||||||
|
(show == DateTime.SHOW_BOTH || show === DateTime.SHOW_DATE) &&
|
||||||
|
date.toLocaleDateString()}
|
||||||
|
{show == DateTime.SHOW_BOTH && " "}
|
||||||
|
{date &&
|
||||||
|
(show == DateTime.SHOW_BOTH || show === DateTime.SHOW_TIME) &&
|
||||||
|
date.toLocaleTimeString()}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,20 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { selectPlatform, selectCurrentModal } from "selectors/app";
|
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
||||||
import {
|
|
||||||
makeSelectFileInfoForUri,
|
|
||||||
makeSelectDownloadingForUri,
|
|
||||||
makeSelectLoadingForUri,
|
|
||||||
} from "selectors/file_info";
|
|
||||||
import { makeSelectIsAvailableForUri } from "selectors/availability";
|
|
||||||
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
||||||
import { doCloseModal, doOpenModal } from "actions/app";
|
import { doOpenModal } from "actions/app";
|
||||||
import { doFetchAvailability } from "actions/availability";
|
import { makeSelectClaimIsMine } from "selectors/claims";
|
||||||
import { doOpenFileInShell, doOpenFileInFolder } from "actions/file_info";
|
|
||||||
import { makeSelectClaimForUriIsMine } from "selectors/claims";
|
|
||||||
import { doPurchaseUri, doLoadVideo, doStartDownload } from "actions/content";
|
|
||||||
import FileActions from "./view";
|
import FileActions from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectFileInfoForUri = makeSelectFileInfoForUri();
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
const selectIsAvailableForUri = makeSelectIsAvailableForUri();
|
|
||||||
const selectDownloadingForUri = makeSelectDownloadingForUri();
|
|
||||||
const selectCostInfoForUri = makeSelectCostInfoForUri();
|
|
||||||
const selectLoadingForUri = makeSelectLoadingForUri();
|
|
||||||
const selectClaimForUriIsMine = makeSelectClaimForUriIsMine();
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
|
||||||
fileInfo: selectFileInfoForUri(state, props),
|
|
||||||
/*availability check is disabled due to poor performance, TBD if it dies forever or requires daemon fix*/
|
/*availability check is disabled due to poor performance, TBD if it dies forever or requires daemon fix*/
|
||||||
isAvailable: true, //selectIsAvailableForUri(state, props),
|
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||||
platform: selectPlatform(state),
|
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
modal: selectCurrentModal(state),
|
|
||||||
downloading: selectDownloadingForUri(state, props),
|
|
||||||
costInfo: selectCostInfoForUri(state, props),
|
|
||||||
loading: selectLoadingForUri(state, props),
|
|
||||||
claimIsMine: selectClaimForUriIsMine(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)),
|
|
||||||
openModal: modal => dispatch(doOpenModal(modal)),
|
|
||||||
startDownload: uri => dispatch(doPurchaseUri(uri, "affirmPurchase")),
|
|
||||||
loadVideo: uri => dispatch(doLoadVideo(uri)),
|
|
||||||
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(FileActions);
|
const perform = dispatch => ({
|
||||||
|
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(FileActions);
|
||||||
|
|
|
@ -1,213 +1,40 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Icon, BusyMessage } from "component/common";
|
|
||||||
import FilePrice from "component/filePrice";
|
|
||||||
import { Modal } from "modal/modal";
|
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import { ToolTip } from "component/tooltip";
|
import FileDownloadLink from "component/fileDownloadLink";
|
||||||
import { DropDownMenu, DropDownMenuItem } from "component/menu";
|
|
||||||
import ModalRemoveFile from "modal/modalRemoveFile";
|
|
||||||
import * as modals from "constants/modal_types";
|
import * as modals from "constants/modal_types";
|
||||||
|
|
||||||
class FileActions extends React.PureComponent {
|
class FileActions extends React.PureComponent {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
forceShowActions: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.checkAvailability(this.props.uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
this.checkAvailability(nextProps.uri);
|
|
||||||
this.restartDownload(nextProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
restartDownload(props) {
|
|
||||||
const { downloading, fileInfo, uri, restartDownload } = props;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!downloading &&
|
|
||||||
fileInfo &&
|
|
||||||
!fileInfo.completed &&
|
|
||||||
fileInfo.written_bytes !== false &&
|
|
||||||
fileInfo.written_bytes < fileInfo.total_bytes
|
|
||||||
) {
|
|
||||||
restartDownload(uri, fileInfo.outpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkAvailability(uri) {
|
|
||||||
if (!this._uri || uri !== this._uri) {
|
|
||||||
this._uri = uri;
|
|
||||||
this.props.checkAvailability(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onShowFileActionsRowClicked() {
|
|
||||||
this.setState({
|
|
||||||
forceShowActions: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onAffirmPurchase() {
|
|
||||||
this.props.closeModal();
|
|
||||||
this.props.loadVideo(this.props.uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { fileInfo, uri, openModal, claimIsMine } = this.props;
|
||||||
fileInfo,
|
|
||||||
isAvailable,
|
|
||||||
platform,
|
|
||||||
downloading,
|
|
||||||
uri,
|
|
||||||
openInFolder,
|
|
||||||
openInShell,
|
|
||||||
modal,
|
|
||||||
openModal,
|
|
||||||
closeModal,
|
|
||||||
startDownload,
|
|
||||||
costInfo,
|
|
||||||
loading,
|
|
||||||
claimIsMine,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const metadata = fileInfo ? fileInfo.metadata : null,
|
const claimId = fileInfo ? fileInfo.claim_id : null,
|
||||||
openInFolderMessage = platform.startsWith("Mac")
|
showDelete = fileInfo && Object.keys(fileInfo).length > 0;
|
||||||
? __("Open in Finder")
|
|
||||||
: __("Open in Folder"),
|
|
||||||
showMenu = fileInfo && Object.keys(fileInfo).length > 0,
|
|
||||||
title = metadata ? metadata.title : uri;
|
|
||||||
|
|
||||||
let content;
|
|
||||||
|
|
||||||
if (loading || downloading) {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (!costInfo) {
|
|
||||||
content = <BusyMessage message={__("Fetching cost info")} />;
|
|
||||||
} else {
|
|
||||||
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 if (!fileInfo) {
|
|
||||||
content = <BusyMessage message={__("Fetching file info")} />;
|
|
||||||
} else {
|
|
||||||
console.log("handle this case of file action props?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="file-actions">
|
<section className="card__actions">
|
||||||
{content}
|
{claimIsMine &&
|
||||||
{showMenu
|
<Link
|
||||||
? <div className="button-set-item">
|
button="text"
|
||||||
<DropDownMenu>
|
icon="icon-edit"
|
||||||
<DropDownMenuItem
|
label={__("Edit")}
|
||||||
key={0}
|
navigate="/publish"
|
||||||
onClick={() => openInFolder(fileInfo)}
|
navigateParams={{ id: claimId }}
|
||||||
label={openInFolderMessage}
|
/>}
|
||||||
|
<FileDownloadLink uri={uri} />
|
||||||
|
<Link
|
||||||
|
button="text"
|
||||||
|
icon="icon-gift"
|
||||||
|
label={__("Support")}
|
||||||
|
navigate="/show"
|
||||||
|
navigateParams={{ uri, tab: "tip" }}
|
||||||
/>
|
/>
|
||||||
<DropDownMenuItem
|
{showDelete &&
|
||||||
key={1}
|
<Link
|
||||||
onClick={() => openModal(modals.CONFIRM_FILE_REMOVE)}
|
button="text"
|
||||||
label={__("Remove...")}
|
icon="icon-trash"
|
||||||
/>
|
label={__("Remove")}
|
||||||
</DropDownMenu>
|
className="card__action--right"
|
||||||
</div>
|
onClick={() => openModal(modals.CONFIRM_FILE_REMOVE, { uri })}
|
||||||
: ""}
|
|
||||||
<Modal
|
|
||||||
type="confirm"
|
|
||||||
isOpen={modal == "affirmPurchase"}
|
|
||||||
contentLabel={__("Confirm Purchase")}
|
|
||||||
onConfirmed={this.onAffirmPurchase.bind(this)}
|
|
||||||
onAborted={closeModal}
|
|
||||||
>
|
|
||||||
{__("This will purchase")} <strong>{title}</strong> {__("for")}{" "}
|
|
||||||
<strong>
|
|
||||||
<FilePrice uri={uri} showFullPrice={true} look="plain" />
|
|
||||||
</strong>{" "}
|
|
||||||
{__("credits")}.
|
|
||||||
</Modal>
|
|
||||||
<Modal
|
|
||||||
isOpen={modal == "timedOut"}
|
|
||||||
contentLabel={__("Download failed")}
|
|
||||||
onConfirmed={closeModal}
|
|
||||||
>
|
|
||||||
{__("LBRY was unable to download the stream")}{" "}{" "}
|
|
||||||
<strong>{title}</strong>.
|
|
||||||
</Modal>
|
|
||||||
{modal == modals.CONFIRM_FILE_REMOVE &&
|
|
||||||
<ModalRemoveFile
|
|
||||||
uri={uri}
|
|
||||||
outpoint={fileInfo.outpoint}
|
|
||||||
title={title}
|
|
||||||
/>}
|
/>}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,32 +9,23 @@ import {
|
||||||
} from "selectors/claims";
|
} from "selectors/claims";
|
||||||
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
||||||
import {
|
import {
|
||||||
makeSelectIsResolvingForUri,
|
makeSelectIsUriResolving,
|
||||||
selectRewardContentClaimIds,
|
selectRewardContentClaimIds,
|
||||||
} from "selectors/content";
|
} from "selectors/content";
|
||||||
import FileCard from "./view";
|
import FileCard from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectClaimForUri = makeSelectClaimForUri();
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
const selectFileInfoForUri = makeSelectFileInfoForUri();
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
const selectMetadataForUri = makeSelectMetadataForUri();
|
|
||||||
const selectResolvingUri = makeSelectIsResolvingForUri();
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
|
||||||
claim: selectClaimForUri(state, props),
|
|
||||||
fileInfo: selectFileInfoForUri(state, props),
|
|
||||||
obscureNsfw: !selectShowNsfw(state),
|
obscureNsfw: !selectShowNsfw(state),
|
||||||
metadata: selectMetadataForUri(state, props),
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
||||||
isResolvingUri: selectResolvingUri(state, props),
|
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(FileCard);
|
export default connect(select, perform)(FileCard);
|
||||||
|
|
23
ui/js/component/fileDetails/index.js
Normal file
23
ui/js/component/fileDetails/index.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import {
|
||||||
|
makeSelectClaimForUri,
|
||||||
|
makeSelectContentTypeForUri,
|
||||||
|
makeSelectMetadataForUri,
|
||||||
|
} from "selectors/claims";
|
||||||
|
import FileDetails from "./view";
|
||||||
|
import { doOpenFileInFolder } from "actions/file_info";
|
||||||
|
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
||||||
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
openFolder: path => dispatch(doOpenFileInFolder(path)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(FileDetails);
|
85
ui/js/component/fileDetails/view.jsx
Normal file
85
ui/js/component/fileDetails/view.jsx
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import lbry from "lbry.js";
|
||||||
|
import FileActions from "component/fileActions";
|
||||||
|
import Link from "component/link";
|
||||||
|
import DateTime from "component/dateTime";
|
||||||
|
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
class FileDetails extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
claim,
|
||||||
|
contentType,
|
||||||
|
fileInfo,
|
||||||
|
metadata,
|
||||||
|
openFolder,
|
||||||
|
uri,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (!claim || !metadata) {
|
||||||
|
return (
|
||||||
|
<div className="card__content">
|
||||||
|
<span className="empty">{__("Empty claim or metadata info.")}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { description, language, license } = metadata;
|
||||||
|
const { height } = claim;
|
||||||
|
const mediaType = lbry.getMediaType(contentType);
|
||||||
|
const directory = fileInfo && fileInfo.download_path
|
||||||
|
? path.dirname(fileInfo.download_path)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FileActions uri={uri} />
|
||||||
|
<div className="card__content card__subtext card__subtext--allow-newlines">
|
||||||
|
<ReactMarkdown
|
||||||
|
source={description || ""}
|
||||||
|
escapeHtml={true}
|
||||||
|
disallowedTypes={["Heading", "HtmlInline", "HtmlBlock"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="card__content">
|
||||||
|
<table className="table-standard table-stretch">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{__("Published on")}</td>
|
||||||
|
<td><DateTime block={height} /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{__("Content-Type")}</td><td>{mediaType}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{__("Language")}</td><td>{language}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{__("License")}</td><td>{license}</td>
|
||||||
|
</tr>
|
||||||
|
{directory &&
|
||||||
|
<tr>
|
||||||
|
<td>{__("Downloaded to")}</td>
|
||||||
|
<td>
|
||||||
|
<Link onClick={() => openFolder(directory)}>
|
||||||
|
{directory}
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
|
</tr>}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
<Link
|
||||||
|
href={`https://lbry.io/dmca?claim_id=${claim.claim_id}`}
|
||||||
|
label={__("report")}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileDetails;
|
29
ui/js/component/fileDownloadLink/index.js
Normal file
29
ui/js/component/fileDownloadLink/index.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import {
|
||||||
|
makeSelectFileInfoForUri,
|
||||||
|
makeSelectDownloadingForUri,
|
||||||
|
makeSelectLoadingForUri,
|
||||||
|
} from "selectors/file_info";
|
||||||
|
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
||||||
|
import { doFetchAvailability } from "actions/availability";
|
||||||
|
import { doOpenFileInShell } from "actions/file_info";
|
||||||
|
import { doPurchaseUri, doStartDownload } from "actions/content";
|
||||||
|
import FileDownloadLink from "./view";
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
|
/*availability check is disabled due to poor performance, TBD if it dies forever or requires daemon fix*/
|
||||||
|
downloading: makeSelectDownloadingForUri(props.uri)(state),
|
||||||
|
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||||
|
loading: makeSelectLoadingForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
checkAvailability: uri => dispatch(doFetchAvailability(uri)),
|
||||||
|
openInShell: path => dispatch(doOpenFileInShell(path)),
|
||||||
|
purchaseUri: uri => dispatch(doPurchaseUri(uri)),
|
||||||
|
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(FileDownloadLink);
|
104
ui/js/component/fileDownloadLink/view.jsx
Normal file
104
ui/js/component/fileDownloadLink/view.jsx
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Icon, BusyMessage } from "component/common";
|
||||||
|
import Link from "component/link";
|
||||||
|
|
||||||
|
class FileDownloadLink extends React.PureComponent {
|
||||||
|
componentWillMount() {
|
||||||
|
this.checkAvailability(this.props.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.checkAvailability(nextProps.uri);
|
||||||
|
this.restartDownload(nextProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
restartDownload(props) {
|
||||||
|
const { downloading, fileInfo, uri, restartDownload } = props;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!downloading &&
|
||||||
|
fileInfo &&
|
||||||
|
!fileInfo.completed &&
|
||||||
|
fileInfo.written_bytes !== false &&
|
||||||
|
fileInfo.written_bytes < fileInfo.total_bytes
|
||||||
|
) {
|
||||||
|
restartDownload(uri, fileInfo.outpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAvailability(uri) {
|
||||||
|
if (!this._uri || uri !== this._uri) {
|
||||||
|
this._uri = uri;
|
||||||
|
this.props.checkAvailability(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
fileInfo,
|
||||||
|
downloading,
|
||||||
|
uri,
|
||||||
|
openInShell,
|
||||||
|
purchaseUri,
|
||||||
|
costInfo,
|
||||||
|
loading,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (loading || downloading) {
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="faux-button-block file-download button-set-item">
|
||||||
|
<div
|
||||||
|
className="faux-button-block file-download__overlay"
|
||||||
|
style={{ width: progress + "%" }}
|
||||||
|
>
|
||||||
|
{labelWithIcon}
|
||||||
|
</div>
|
||||||
|
{labelWithIcon}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (fileInfo === null && !downloading) {
|
||||||
|
if (!costInfo) {
|
||||||
|
return <BusyMessage message={__("Fetching cost info")} />;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
button="text"
|
||||||
|
label={__("Download")}
|
||||||
|
icon="icon-download"
|
||||||
|
onClick={() => {
|
||||||
|
purchaseUri(uri);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (fileInfo && fileInfo.download_path) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
label={__("Open")}
|
||||||
|
button="text"
|
||||||
|
icon="icon-external-link-square"
|
||||||
|
onClick={() => openInShell(fileInfo.download_path)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileDownloadLink;
|
|
@ -88,6 +88,7 @@ class FileList extends React.PureComponent {
|
||||||
key={fileInfo.outpoint || fileInfo.claim_id}
|
key={fileInfo.outpoint || fileInfo.claim_id}
|
||||||
uri={uri}
|
uri={uri}
|
||||||
hidePrice={true}
|
hidePrice={true}
|
||||||
|
showActions={true}
|
||||||
showEmpty={this.props.fileTileShowEmpty}
|
showEmpty={this.props.fileTileShowEmpty}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -49,7 +49,17 @@ const FileListSearchResults = props => {
|
||||||
|
|
||||||
class FileListSearch extends React.PureComponent {
|
class FileListSearch extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.props.search(this.props.query);
|
this.doSearch(this.props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(props) {
|
||||||
|
if (props.query != this.props.query) {
|
||||||
|
this.doSearch(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearch(props) {
|
||||||
|
this.props.search(props.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -8,23 +8,15 @@ import {
|
||||||
import { makeSelectClaimForUri } from "selectors/claims";
|
import { makeSelectClaimForUri } from "selectors/claims";
|
||||||
import FilePrice from "./view";
|
import FilePrice from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectCostInfoForUri = makeSelectCostInfoForUri();
|
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||||
const selectFetchingCostInfoForUri = makeSelectFetchingCostInfoForUri();
|
fetching: makeSelectFetchingCostInfoForUri(props.uri)(state),
|
||||||
const selectClaim = makeSelectClaimForUri();
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
});
|
||||||
const select = (state, props) => ({
|
|
||||||
costInfo: selectCostInfoForUri(state, props),
|
|
||||||
fetching: selectFetchingCostInfoForUri(state, props),
|
|
||||||
claim: selectClaim(state, props),
|
|
||||||
});
|
|
||||||
|
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
// cancelFetchCostInfo: (uri) => dispatch(doCancelFetchCostInfoForUri(uri))
|
// cancelFetchCostInfo: (uri) => dispatch(doCancelFetchCostInfoForUri(uri))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(FilePrice);
|
export default connect(select, perform)(FilePrice);
|
||||||
|
|
|
@ -9,32 +9,23 @@ import {
|
||||||
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
||||||
import { selectShowNsfw } from "selectors/settings";
|
import { selectShowNsfw } from "selectors/settings";
|
||||||
import {
|
import {
|
||||||
makeSelectIsResolvingForUri,
|
makeSelectIsUriResolving,
|
||||||
selectRewardContentClaimIds,
|
selectRewardContentClaimIds,
|
||||||
} from "selectors/content";
|
} from "selectors/content";
|
||||||
import FileTile from "./view";
|
import FileTile from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectClaimForUri = makeSelectClaimForUri();
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
const selectFileInfoForUri = makeSelectFileInfoForUri();
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
const selectMetadataForUri = makeSelectMetadataForUri();
|
|
||||||
const selectResolvingUri = makeSelectIsResolvingForUri();
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
|
||||||
claim: selectClaimForUri(state, props),
|
|
||||||
fileInfo: selectFileInfoForUri(state, props),
|
|
||||||
obscureNsfw: !selectShowNsfw(state),
|
obscureNsfw: !selectShowNsfw(state),
|
||||||
metadata: selectMetadataForUri(state, props),
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
isResolvingUri: selectResolvingUri(state, props),
|
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||||
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
||||||
});
|
});
|
||||||
|
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(FileTile);
|
export default connect(select, perform)(FileTile);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import lbryuri from "lbryuri.js";
|
import lbryuri from "lbryuri.js";
|
||||||
import CardMedia from "component/cardMedia";
|
import CardMedia from "component/cardMedia";
|
||||||
|
import FileActions from "component/fileActions";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import { TruncatedText } from "component/common.js";
|
import { TruncatedText } from "component/common.js";
|
||||||
import FilePrice from "component/filePrice";
|
import FilePrice from "component/filePrice";
|
||||||
|
@ -53,6 +54,7 @@ class FileTile extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
claim,
|
claim,
|
||||||
|
showActions,
|
||||||
metadata,
|
metadata,
|
||||||
isResolvingUri,
|
isResolvingUri,
|
||||||
showEmpty,
|
showEmpty,
|
||||||
|
@ -104,7 +106,7 @@ class FileTile extends React.PureComponent {
|
||||||
onMouseEnter={this.handleMouseOver.bind(this)}
|
onMouseEnter={this.handleMouseOver.bind(this)}
|
||||||
onMouseLeave={this.handleMouseOut.bind(this)}
|
onMouseLeave={this.handleMouseOut.bind(this)}
|
||||||
>
|
>
|
||||||
<Link onClick={onClick} className="card__link">
|
<div onClick={onClick} className="card__link">
|
||||||
<div className={"card__inner file-tile__row"}>
|
<div className={"card__inner file-tile__row"}>
|
||||||
<CardMedia title={title} thumbnail={thumbnail} />
|
<CardMedia title={title} thumbnail={thumbnail} />
|
||||||
<div className="file-tile__content">
|
<div className="file-tile__content">
|
||||||
|
@ -116,14 +118,15 @@ class FileTile extends React.PureComponent {
|
||||||
<TruncatedText lines={1}>{title}</TruncatedText>
|
<TruncatedText lines={1}>{title}</TruncatedText>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
{description &&
|
||||||
<div className="card__content card__subtext">
|
<div className="card__content card__subtext">
|
||||||
<TruncatedText lines={3}>
|
<TruncatedText lines={!showActions ? 4 : 2}>
|
||||||
{description}
|
{description}
|
||||||
</TruncatedText>
|
</TruncatedText>
|
||||||
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
|
||||||
{this.state.showNsfwHelp && <NsfwOverlay />}
|
{this.state.showNsfwHelp && <NsfwOverlay />}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import FormField from "component/formField";
|
import FormField from "component/formField";
|
||||||
|
import { Icon } from "component/common.js";
|
||||||
|
|
||||||
let formFieldCounter = 0;
|
let formFieldCounter = 0;
|
||||||
|
|
||||||
|
@ -9,6 +10,29 @@ export function formFieldId() {
|
||||||
return "form-field-" + ++formFieldCounter;
|
return "form-field-" + ++formFieldCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Form extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
onSubmit: React.PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.props.onSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<form onSubmit={event => this.handleSubmit(event)}>
|
||||||
|
{this.props.children}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class FormRow extends React.PureComponent {
|
export class FormRow extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
label: React.PropTypes.oneOfType([
|
label: React.PropTypes.oneOfType([
|
||||||
|
@ -74,11 +98,6 @@ export class FormRow extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptions() {
|
getOptions() {
|
||||||
if (!this._field || !this._field.getOptions) {
|
|
||||||
console.log(this);
|
|
||||||
console.log(this._field);
|
|
||||||
console.log(this._field.getOptions);
|
|
||||||
}
|
|
||||||
return this._field.getOptions();
|
return this._field.getOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,3 +155,27 @@ export class FormRow extends React.PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Submit = props => {
|
||||||
|
const { title, label, icon, disabled } = props;
|
||||||
|
|
||||||
|
const className =
|
||||||
|
"button-block" +
|
||||||
|
" button-primary" +
|
||||||
|
" button-set-item" +
|
||||||
|
" button--submit" +
|
||||||
|
(disabled ? " disabled" : "");
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<span className="button__content">
|
||||||
|
{"icon" in props ? <Icon icon={icon} fixed={true} /> : null}
|
||||||
|
{label ? <span className="button-label">{label}</span> : null}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button type="submit" className={className} title={title}>
|
||||||
|
{content}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -47,9 +47,9 @@ class InviteList extends React.PureComponent {
|
||||||
<td className="text-center">
|
<td className="text-center">
|
||||||
{invitee.invite_reward_claimed
|
{invitee.invite_reward_claimed
|
||||||
? <Icon icon="icon-check" />
|
? <Icon icon="icon-check" />
|
||||||
: invitee.invite_accepted
|
: invitee.invite_reward_claimable
|
||||||
? <RewardLink
|
? <RewardLink
|
||||||
label={__("Claim")}
|
label={__("claim")}
|
||||||
reward_type={rewards.TYPE_REFERRAL}
|
reward_type={rewards.TYPE_REFERRAL}
|
||||||
/>
|
/>
|
||||||
: <span className="empty">
|
: <span className="empty">
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { BusyMessage, CreditAmount } from "component/common";
|
import { BusyMessage, CreditAmount } from "component/common";
|
||||||
import Link from "component/link";
|
import { Form, FormRow, Submit } from "component/form.js";
|
||||||
import { FormRow } from "component/form.js";
|
|
||||||
|
|
||||||
class FormInviteNew extends React.PureComponent {
|
class FormInviteNew extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -18,16 +17,16 @@ class FormInviteNew extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(event) {
|
handleSubmit() {
|
||||||
event.preventDefault();
|
const { email } = this.state;
|
||||||
this.props.inviteNew(this.state.email);
|
this.props.inviteNew(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending } = this.props;
|
const { errorMessage, isPending } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form>
|
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||||
<FormRow
|
<FormRow
|
||||||
type="text"
|
type="text"
|
||||||
label="Email"
|
label="Email"
|
||||||
|
@ -40,16 +39,9 @@ class FormInviteNew extends React.PureComponent {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="form-row-submit">
|
<div className="form-row-submit">
|
||||||
<Link
|
<Submit label={__("Send Invite")} disabled={isPending} />
|
||||||
button="primary"
|
|
||||||
label={__("Send Invite")}
|
|
||||||
disabled={isPending}
|
|
||||||
onClick={event => {
|
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { doNavigate } from "actions/navigation";
|
||||||
import Link from "./view";
|
import Link from "./view";
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
doNavigate: path => dispatch(doNavigate(path)),
|
doNavigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, perform)(Link);
|
export default connect(null, perform)(Link);
|
||||||
|
|
|
@ -8,11 +8,11 @@ const Link = props => {
|
||||||
style,
|
style,
|
||||||
label,
|
label,
|
||||||
icon,
|
icon,
|
||||||
badge,
|
|
||||||
button,
|
button,
|
||||||
disabled,
|
disabled,
|
||||||
children,
|
children,
|
||||||
navigate,
|
navigate,
|
||||||
|
navigateParams,
|
||||||
doNavigate,
|
doNavigate,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ const Link = props => {
|
||||||
|
|
||||||
const onClick = !props.onClick && navigate
|
const onClick = !props.onClick && navigate
|
||||||
? () => {
|
? () => {
|
||||||
doNavigate(navigate);
|
doNavigate(navigate, navigateParams || {});
|
||||||
}
|
}
|
||||||
: props.onClick;
|
: props.onClick;
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ const Link = props => {
|
||||||
<span {...("button" in props ? { className: "button__content" } : {})}>
|
<span {...("button" in props ? { className: "button__content" } : {})}>
|
||||||
{"icon" in props ? <Icon icon={icon} fixed={true} /> : null}
|
{"icon" in props ? <Icon icon={icon} fixed={true} /> : null}
|
||||||
{label ? <span className="link-label">{label}</span> : null}
|
{label ? <span className="link-label">{label}</span> : null}
|
||||||
{"badge" in props ? <span className="badge">{badge}</span> : null}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import Link from "./view";
|
import LinkTransaction from "./view";
|
||||||
|
|
||||||
export default connect(null, null)(Link);
|
export default connect(null, null)(LinkTransaction);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
||||||
import lbry from "lbry";
|
import lbry from "lbry";
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
import FormField from "component/formField";
|
import FormField from "component/formField";
|
||||||
import { FormRow } from "component/form.js";
|
import { Form, FormRow, Submit } from "component/form.js";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import FormFieldPrice from "component/formFieldPrice";
|
import FormFieldPrice from "component/formFieldPrice";
|
||||||
import Modal from "modal/modal";
|
import Modal from "modal/modal";
|
||||||
|
@ -19,6 +19,7 @@ class PublishForm extends React.PureComponent {
|
||||||
this._defaultPaidPrice = 0.01;
|
this._defaultPaidPrice = 0.01;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
id: null,
|
||||||
rawName: "",
|
rawName: "",
|
||||||
name: "",
|
name: "",
|
||||||
bid: 10,
|
bid: 10,
|
||||||
|
@ -48,6 +49,7 @@ class PublishForm extends React.PureComponent {
|
||||||
isFee: false,
|
isFee: false,
|
||||||
customUrl: false,
|
customUrl: false,
|
||||||
source: null,
|
source: null,
|
||||||
|
mode: "publish",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,11 +59,7 @@ class PublishForm extends React.PureComponent {
|
||||||
if (!fetchingChannels) fetchChannelListMine();
|
if (!fetchingChannels) fetchChannelListMine();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(event) {
|
handleSubmit() {
|
||||||
if (typeof event !== "undefined") {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
submitting: true,
|
submitting: true,
|
||||||
});
|
});
|
||||||
|
@ -187,6 +185,14 @@ class PublishForm extends React.PureComponent {
|
||||||
return !!myClaims.find(claim => claim.name === name);
|
return !!myClaims.find(claim => claim.name === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleEditClaim() {
|
||||||
|
const claimInfo = this.claim() || this.myClaimInfo();
|
||||||
|
|
||||||
|
if (claimInfo) {
|
||||||
|
this.handlePrefillClaim(claimInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
topClaimIsMine() {
|
topClaimIsMine() {
|
||||||
const myClaimInfo = this.myClaimInfo();
|
const myClaimInfo = this.myClaimInfo();
|
||||||
const { claimsByUri } = this.props;
|
const { claimsByUri } = this.props;
|
||||||
|
@ -203,10 +209,10 @@ class PublishForm extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
myClaimInfo() {
|
myClaimInfo() {
|
||||||
const { name } = this.state;
|
const { id } = this.state;
|
||||||
|
|
||||||
return Object.values(this.props.myClaims).find(
|
return Object.values(this.props.myClaims).find(
|
||||||
claim => claim.name === name
|
claim => claim.claim_id === id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,6 +232,7 @@ class PublishForm extends React.PureComponent {
|
||||||
name: "",
|
name: "",
|
||||||
uri: "",
|
uri: "",
|
||||||
prefillDone: false,
|
prefillDone: false,
|
||||||
|
mode: "publish",
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -247,6 +254,7 @@ class PublishForm extends React.PureComponent {
|
||||||
rawName: rawName,
|
rawName: rawName,
|
||||||
name: name,
|
name: name,
|
||||||
prefillDone: false,
|
prefillDone: false,
|
||||||
|
mode: "publish",
|
||||||
uri,
|
uri,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -261,9 +269,10 @@ class PublishForm extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePrefillClicked() {
|
handlePrefillClaim(claimInfo) {
|
||||||
const claimInfo = this.myClaimInfo();
|
const { claim_id, name, channel_name, amount } = claimInfo;
|
||||||
const { source } = claimInfo.value.stream;
|
const { source, metadata } = claimInfo.value.stream;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
license,
|
license,
|
||||||
licenseUrl,
|
licenseUrl,
|
||||||
|
@ -272,16 +281,21 @@ class PublishForm extends React.PureComponent {
|
||||||
description,
|
description,
|
||||||
language,
|
language,
|
||||||
nsfw,
|
nsfw,
|
||||||
} = claimInfo.value.stream.metadata;
|
} = metadata;
|
||||||
|
|
||||||
let newState = {
|
let newState = {
|
||||||
|
id: claim_id,
|
||||||
|
channel: channel_name || "anonymous",
|
||||||
|
bid: amount,
|
||||||
meta_title: title,
|
meta_title: title,
|
||||||
meta_thumbnail: thumbnail,
|
meta_thumbnail: thumbnail,
|
||||||
meta_description: description,
|
meta_description: description,
|
||||||
meta_language: language,
|
meta_language: language,
|
||||||
meta_nsfw: nsfw,
|
meta_nsfw: nsfw,
|
||||||
|
mode: "edit",
|
||||||
prefillDone: true,
|
prefillDone: true,
|
||||||
bid: claimInfo.amount,
|
rawName: name,
|
||||||
|
name,
|
||||||
source,
|
source,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -375,6 +389,7 @@ class PublishForm extends React.PureComponent {
|
||||||
|
|
||||||
handleChannelChange(channelName) {
|
handleChannelChange(channelName) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
mode: "publish",
|
||||||
channel: channelName,
|
channel: channelName,
|
||||||
});
|
});
|
||||||
const nameChanged = () => this.nameChanged(this.state.rawName);
|
const nameChanged = () => this.nameChanged(this.state.rawName);
|
||||||
|
@ -412,6 +427,13 @@ class PublishForm extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.props.fetchClaimListMine();
|
this.props.fetchClaimListMine();
|
||||||
this._updateChannelList();
|
this._updateChannelList();
|
||||||
|
|
||||||
|
const { id } = this.props.params;
|
||||||
|
this.setState({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.handleEditClaim();
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileChange() {
|
onFileChange() {
|
||||||
|
@ -436,37 +458,38 @@ class PublishForm extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
getNameBidHelpText() {
|
getNameBidHelpText() {
|
||||||
if (this.state.prefillDone) {
|
const { prefillDone, name, uri } = this.state;
|
||||||
|
const { resolvingUris } = this.props;
|
||||||
|
const claim = this.claim();
|
||||||
|
|
||||||
|
if (prefillDone) {
|
||||||
return __("Existing claim data was prefilled");
|
return __("Existing claim data was prefilled");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (uri && resolvingUris.indexOf(uri) !== -1 && claim === undefined) {
|
||||||
this.state.uri &&
|
|
||||||
this.props.resolvingUris.indexOf(this.state.uri) !== -1 &&
|
|
||||||
this.claim() === undefined
|
|
||||||
) {
|
|
||||||
return __("Checking...");
|
return __("Checking...");
|
||||||
} else if (!this.state.name) {
|
} else if (!name) {
|
||||||
return __("Select a URL for this publish.");
|
return __("Select a URL for this publish.");
|
||||||
} else if (!this.claim()) {
|
} else if (!claim) {
|
||||||
return __("This URL is unused.");
|
return __("This URL is unused.");
|
||||||
} else if (this.myClaimExists() && !this.state.prefillDone) {
|
} else if (this.myClaimExists() && !prefillDone) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{__("You already have a claim with this name.")}{" "}
|
{__("You already have a claim with this name.")}{" "}
|
||||||
<Link
|
<Link
|
||||||
label={__("Use data from my existing claim")}
|
label={__("Edit existing claim")}
|
||||||
onClick={() => this.handlePrefillClicked()}
|
onClick={() => this.handleEditClaim()}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else if (this.claim()) {
|
} else if (claim) {
|
||||||
if (this.topClaimValue() === 1) {
|
const topClaimValue = this.topClaimValue();
|
||||||
|
if (topClaimValue === 1) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{__(
|
{__(
|
||||||
'A deposit of at least one credit is required to win "%s". However, you can still get a permanent URL for any amount.',
|
'A deposit of at least one credit is required to win "%s". However, you can still get a permanent URL for any amount.',
|
||||||
this.state.name
|
name
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
@ -475,8 +498,8 @@ class PublishForm extends React.PureComponent {
|
||||||
<span>
|
<span>
|
||||||
{__(
|
{__(
|
||||||
'A deposit of at least "%s" credits is required to win "%s". However, you can still get a permanent URL for any amount.',
|
'A deposit of at least "%s" credits is required to win "%s". However, you can still get a permanent URL for any amount.',
|
||||||
this.topClaimValue(),
|
topClaimValue,
|
||||||
this.state.name
|
name
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
@ -493,17 +516,21 @@ class PublishForm extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { mode, submitting } = this.state;
|
||||||
|
|
||||||
const lbcInputHelp = __(
|
const lbcInputHelp = __(
|
||||||
"This LBC remains yours and the deposit can be undone at any time."
|
"This LBC remains yours and the deposit can be undone at any time."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let submitLabel = !submitting ? __("Publish") : __("Publishing...");
|
||||||
|
|
||||||
|
if (mode === "edit") {
|
||||||
|
submitLabel = !submitting ? __("Update") : __("Updating...");
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<form
|
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||||
onSubmit={event => {
|
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__title-primary">
|
<div className="card__title-primary">
|
||||||
<h4>{__("Content")}</h4>
|
<h4>{__("Content")}</h4>
|
||||||
|
@ -837,14 +864,10 @@ class PublishForm extends React.PureComponent {
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div className="card-series-submit">
|
<div className="card-series-submit">
|
||||||
<Link
|
<Submit
|
||||||
button="primary"
|
|
||||||
label={
|
label={
|
||||||
!this.state.submitting ? __("Publish") : __("Publishing...")
|
!this.state.submitting ? __("Publish") : __("Publishing...")
|
||||||
}
|
}
|
||||||
onClick={event => {
|
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
disabled={
|
disabled={
|
||||||
this.state.submitting ||
|
this.state.submitting ||
|
||||||
(this.state.uri &&
|
(this.state.uri &&
|
||||||
|
@ -859,9 +882,8 @@ class PublishForm extends React.PureComponent {
|
||||||
onClick={this.props.back}
|
onClick={this.props.back}
|
||||||
label={__("Cancel")}
|
label={__("Cancel")}
|
||||||
/>
|
/>
|
||||||
<input type="submit" className="hidden" />
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</Form>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={this.state.modal == "publishStarted"}
|
isOpen={this.state.modal == "publishStarted"}
|
||||||
|
|
|
@ -46,7 +46,7 @@ const Router = props => {
|
||||||
search: <SearchPage params={params} />,
|
search: <SearchPage params={params} />,
|
||||||
send: <SendCreditsPage params={params} />,
|
send: <SendCreditsPage params={params} />,
|
||||||
settings: <SettingsPage params={params} />,
|
settings: <SettingsPage params={params} />,
|
||||||
show: <ShowPage params={params} />,
|
show: <ShowPage {...params} />,
|
||||||
wallet: <WalletPage params={params} />,
|
wallet: <WalletPage params={params} />,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
10
ui/js/component/theme/index.js
Normal file
10
ui/js/component/theme/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { selectThemePath } from "selectors/settings.js";
|
||||||
|
import Theme from "./view";
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
themePath: selectThemePath(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, null)(Theme);
|
20
ui/js/component/theme/view.jsx
Normal file
20
ui/js/component/theme/view.jsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const Theme = props => {
|
||||||
|
const { themePath } = props;
|
||||||
|
|
||||||
|
if (!themePath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<link
|
||||||
|
href={themePath}
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
media="screen,print"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Theme;
|
|
@ -1,5 +1,15 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { doNavigate } from "actions/navigation";
|
||||||
|
import { selectClaimedRewardsByTransactionId } from "selectors/rewards";
|
||||||
import TransactionList from "./view";
|
import TransactionList from "./view";
|
||||||
|
|
||||||
export default connect(null, null)(TransactionList);
|
const select = state => ({
|
||||||
|
rewards: selectClaimedRewardsByTransactionId(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, perform)(TransactionList);
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import React from "react";
|
||||||
|
import LinkTransaction from "component/linkTransaction";
|
||||||
|
import { CreditAmount } from "component/common";
|
||||||
|
import DateTime from "component/dateTime";
|
||||||
|
import Link from "component/link";
|
||||||
|
import lbryuri from "lbryuri";
|
||||||
|
|
||||||
|
class TransactionListItem extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const { reward, transaction } = this.props;
|
||||||
|
const {
|
||||||
|
amount,
|
||||||
|
claim_id: claimId,
|
||||||
|
claim_name: name,
|
||||||
|
date,
|
||||||
|
fee,
|
||||||
|
txid,
|
||||||
|
type,
|
||||||
|
} = transaction;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{date
|
||||||
|
? <div>
|
||||||
|
<DateTime date={date} show={DateTime.SHOW_DATE} />
|
||||||
|
<div className="meta">
|
||||||
|
<DateTime date={date} show={DateTime.SHOW_TIME} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: <span className="empty">
|
||||||
|
{__("(Transaction pending)")}
|
||||||
|
</span>}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<CreditAmount
|
||||||
|
amount={amount}
|
||||||
|
look="plain"
|
||||||
|
label={false}
|
||||||
|
showPlus={true}
|
||||||
|
precision={8}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
{fee != 0 &&
|
||||||
|
<CreditAmount
|
||||||
|
amount={fee}
|
||||||
|
look="fee"
|
||||||
|
label={false}
|
||||||
|
precision={8}
|
||||||
|
/>}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{type}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{reward &&
|
||||||
|
<Link navigate="/rewards">
|
||||||
|
{__("Reward: %s", reward.reward_title)}
|
||||||
|
</Link>}
|
||||||
|
{name &&
|
||||||
|
claimId &&
|
||||||
|
<Link
|
||||||
|
className="button-text"
|
||||||
|
navigate="/show"
|
||||||
|
navigateParams={{ uri: lbryuri.build({ name, claimId }) }}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Link>}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<LinkTransaction id={txid} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TransactionListItem;
|
|
@ -1,57 +1,82 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import LinkTransaction from "component/linkTransaction";
|
import TransactionListItem from "./internal/TransactionListItem";
|
||||||
import { CreditAmount } from "component/common";
|
import FormField from "component/formField";
|
||||||
|
|
||||||
const TransactionList = props => {
|
class TransactionList extends React.PureComponent {
|
||||||
const { emptyMessage, transactions } = props;
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
if (!transactions || !transactions.length) {
|
this.state = {
|
||||||
return (
|
filter: null,
|
||||||
<div className="empty">
|
};
|
||||||
{emptyMessage || __("No transactions to list.")}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleFilterChanged(event) {
|
||||||
|
this.setState({
|
||||||
|
filter: event.target.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
filterTransaction(transaction) {
|
||||||
|
const { filter } = this.state;
|
||||||
|
|
||||||
|
return !filter || filter == transaction.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { emptyMessage, rewards, transactions } = this.props;
|
||||||
|
|
||||||
|
let transactionList = transactions.filter(
|
||||||
|
this.filterTransaction.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="table-standard table-stretch">
|
<div>
|
||||||
|
{(transactionList.length || this.state.filter) &&
|
||||||
|
<span className="sort-section">
|
||||||
|
{__("Filter")} {" "}
|
||||||
|
<FormField
|
||||||
|
type="select"
|
||||||
|
onChange={this.handleFilterChanged.bind(this)}
|
||||||
|
>
|
||||||
|
<option value="">{__("All")}</option>
|
||||||
|
<option value="spend">{__("Spends")}</option>
|
||||||
|
<option value="receive">{__("Receives")}</option>
|
||||||
|
<option value="publish">{__("Publishes")}</option>
|
||||||
|
<option value="channel">{__("Channels")}</option>
|
||||||
|
<option value="tip">{__("Tips")}</option>
|
||||||
|
<option value="support">{__("Supports")}</option>
|
||||||
|
<option value="update">{__("Updates")}</option>
|
||||||
|
</FormField>
|
||||||
|
</span>}
|
||||||
|
{!transactionList.length &&
|
||||||
|
<div className="empty">
|
||||||
|
{emptyMessage || __("No transactions to list.")}
|
||||||
|
</div>}
|
||||||
|
{Boolean(transactionList.length) &&
|
||||||
|
<table className="table-standard table-transactions table-stretch">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{__("Date")}</th>
|
<th>{__("Date")}</th>
|
||||||
<th>{__("Amount")}</th>
|
<th>{__("Amount (Fee)")}</th>
|
||||||
|
<th>{__("Type")} </th>
|
||||||
|
<th>{__("Details")} </th>
|
||||||
<th>{__("Transaction")}</th>
|
<th>{__("Transaction")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{transactions.map(item => {
|
{transactionList.map(t =>
|
||||||
return (
|
<TransactionListItem
|
||||||
<tr key={item.id}>
|
key={`${t.txid}:${t.nout}`}
|
||||||
<td>
|
transaction={t}
|
||||||
{item.date
|
reward={rewards && rewards[t.txid]}
|
||||||
? item.date.toLocaleDateString() +
|
/>
|
||||||
" " +
|
)}
|
||||||
item.date.toLocaleTimeString()
|
|
||||||
: <span className="empty">
|
|
||||||
{__("(Transaction pending)")}
|
|
||||||
</span>}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<CreditAmount
|
|
||||||
amount={item.amount}
|
|
||||||
look="plain"
|
|
||||||
showPlus={true}
|
|
||||||
precision={8}
|
|
||||||
/>{" "}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<LinkTransaction id={item.id} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default TransactionList;
|
export default TransactionList;
|
||||||
|
|
|
@ -1,25 +1,19 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { makeSelectIsResolvingForUri } from "selectors/content";
|
import { doResolveUri } from "actions/content";
|
||||||
|
import { makeSelectIsUriResolving } from "selectors/content";
|
||||||
import { makeSelectClaimForUri } from "selectors/claims";
|
import { makeSelectClaimForUri } from "selectors/claims";
|
||||||
import UriIndicator from "./view";
|
import UriIndicator from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectClaim = makeSelectClaimForUri(),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
selectIsResolving = makeSelectIsResolvingForUri();
|
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||||
|
|
||||||
const select = (state, props) => ({
|
|
||||||
claim: selectClaim(state, props),
|
|
||||||
isResolvingUri: selectIsResolving(state, props),
|
|
||||||
uri: lbryuri.normalize(props.uri),
|
uri: lbryuri.normalize(props.uri),
|
||||||
});
|
});
|
||||||
|
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(UriIndicator);
|
export default connect(select, perform)(UriIndicator);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Icon } from "component/common";
|
import { Icon } from "component/common";
|
||||||
|
import Link from "component/link";
|
||||||
|
import lbryuri from "lbryuri.js";
|
||||||
|
|
||||||
class UriIndicator extends React.PureComponent {
|
class UriIndicator extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
@ -19,7 +21,7 @@ class UriIndicator extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { claim, uri, isResolvingUri } = this.props;
|
const { claim, link, uri, isResolvingUri } = this.props;
|
||||||
|
|
||||||
if (isResolvingUri && !claim) {
|
if (isResolvingUri && !claim) {
|
||||||
return <span className="empty">Validating...</span>;
|
return <span className="empty">Validating...</span>;
|
||||||
|
@ -33,21 +35,30 @@ class UriIndicator extends React.PureComponent {
|
||||||
channel_name: channelName,
|
channel_name: channelName,
|
||||||
has_signature: hasSignature,
|
has_signature: hasSignature,
|
||||||
signature_is_valid: signatureIsValid,
|
signature_is_valid: signatureIsValid,
|
||||||
|
value,
|
||||||
} = claim;
|
} = claim;
|
||||||
|
const channelClaimId =
|
||||||
|
value &&
|
||||||
|
value.publisherSignature &&
|
||||||
|
value.publisherSignature.certificateId;
|
||||||
|
|
||||||
if (!hasSignature || !channelName) {
|
if (!hasSignature || !channelName) {
|
||||||
return <span className="empty">Anonymous</span>;
|
return <span className="empty">Anonymous</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let icon, modifier;
|
let icon, channelLink, modifier;
|
||||||
|
|
||||||
if (signatureIsValid) {
|
if (signatureIsValid) {
|
||||||
modifier = "valid";
|
modifier = "valid";
|
||||||
|
channelLink = link
|
||||||
|
? lbryuri.build({ channelName, claimId: channelClaimId }, false)
|
||||||
|
: false;
|
||||||
} else {
|
} else {
|
||||||
icon = "icon-times-circle";
|
icon = "icon-times-circle";
|
||||||
modifier = "invalid";
|
modifier = "invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const inner = (
|
||||||
<span>
|
<span>
|
||||||
{channelName} {" "}
|
{channelName} {" "}
|
||||||
{!signatureIsValid
|
{!signatureIsValid
|
||||||
|
@ -58,6 +69,16 @@ class UriIndicator extends React.PureComponent {
|
||||||
: ""}
|
: ""}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!channelLink) {
|
||||||
|
return inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link navigate="/show" navigateParams={{ uri: channelLink }}>
|
||||||
|
{inner}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import { FormRow } from "component/form.js";
|
import { Form, FormRow, Submit } from "component/form.js";
|
||||||
|
|
||||||
class UserEmailNew extends React.PureComponent {
|
class UserEmailNew extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -17,20 +17,16 @@ class UserEmailNew extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(event) {
|
handleSubmit() {
|
||||||
event.preventDefault();
|
const { email } = this.state;
|
||||||
this.props.addUserEmail(this.state.email);
|
this.props.addUserEmail(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending } = this.props;
|
const { errorMessage, isPending } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||||
onSubmit={event => {
|
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>
|
<p>
|
||||||
{__(
|
{__(
|
||||||
"This process is required to prevent abuse of the rewards program."
|
"This process is required to prevent abuse of the rewards program."
|
||||||
|
@ -53,16 +49,9 @@ class UserEmailNew extends React.PureComponent {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="form-row-submit">
|
<div className="form-row-submit">
|
||||||
<Link
|
<Submit label="Next" disabled={isPending} />
|
||||||
button="primary"
|
|
||||||
label="Next"
|
|
||||||
disabled={isPending}
|
|
||||||
onClick={event => {
|
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import { FormRow } from "component/form.js";
|
import { Form, FormRow, Submit } from "component/form.js";
|
||||||
|
|
||||||
class UserEmailVerify extends React.PureComponent {
|
class UserEmailVerify extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -17,19 +17,15 @@ class UserEmailVerify extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(event) {
|
handleSubmit() {
|
||||||
event.preventDefault();
|
const { code } = this.state;
|
||||||
this.props.verifyUserEmail(this.state.code);
|
this.props.verifyUserEmail(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending } = this.props;
|
const { errorMessage, isPending } = this.props;
|
||||||
return (
|
return (
|
||||||
<form
|
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||||
onSubmit={event => {
|
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>{__("Please enter the verification code emailed to you.")}</p>
|
<p>{__("Please enter the verification code emailed to you.")}</p>
|
||||||
<FormRow
|
<FormRow
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -50,16 +46,9 @@ class UserEmailVerify extends React.PureComponent {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-row-submit form-row-submit--with-footer">
|
<div className="form-row-submit form-row-submit--with-footer">
|
||||||
<Link
|
<Submit label={__("Verify")} disabled={this.state.submitting} />
|
||||||
button="primary"
|
|
||||||
label={__("Verify")}
|
|
||||||
disabled={this.state.submitting}
|
|
||||||
onClick={event => {
|
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doCloseModal } from "actions/app";
|
|
||||||
import { doChangeVolume } from "actions/app";
|
import { doChangeVolume } from "actions/app";
|
||||||
import { selectCurrentModal, selectVolume } from "selectors/app";
|
import { selectVolume } from "selectors/app";
|
||||||
import { doPurchaseUri, doLoadVideo } from "actions/content";
|
import { doPlayUri, doSetPlayingUri } from "actions/content";
|
||||||
import {
|
import {
|
||||||
makeSelectMetadataForUri,
|
makeSelectMetadataForUri,
|
||||||
makeSelectContentTypeForUri,
|
makeSelectContentTypeForUri,
|
||||||
|
@ -16,35 +15,24 @@ import {
|
||||||
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
||||||
import { selectShowNsfw } from "selectors/settings";
|
import { selectShowNsfw } from "selectors/settings";
|
||||||
import Video from "./view";
|
import Video from "./view";
|
||||||
|
import { selectPlayingUri } from "selectors/content";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectCostInfo = makeSelectCostInfoForUri();
|
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||||
const selectFileInfo = makeSelectFileInfoForUri();
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
const selectIsLoading = makeSelectLoadingForUri();
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
const selectIsDownloading = makeSelectDownloadingForUri();
|
|
||||||
const selectMetadata = makeSelectMetadataForUri();
|
|
||||||
const selectContentType = makeSelectContentTypeForUri();
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
|
||||||
costInfo: selectCostInfo(state, props),
|
|
||||||
fileInfo: selectFileInfo(state, props),
|
|
||||||
metadata: selectMetadata(state, props),
|
|
||||||
obscureNsfw: !selectShowNsfw(state),
|
obscureNsfw: !selectShowNsfw(state),
|
||||||
modal: selectCurrentModal(state),
|
isLoading: makeSelectLoadingForUri(props.uri)(state),
|
||||||
isLoading: selectIsLoading(state, props),
|
isDownloading: makeSelectDownloadingForUri(props.uri)(state),
|
||||||
isDownloading: selectIsDownloading(state, props),
|
playingUri: selectPlayingUri(state),
|
||||||
contentType: selectContentType(state, props),
|
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
||||||
volume: selectVolume(state, props),
|
volume: selectVolume(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
loadVideo: uri => dispatch(doLoadVideo(uri)),
|
play: uri => dispatch(doPlayUri(uri)),
|
||||||
purchaseUri: uri => dispatch(doPurchaseUri(uri, "affirmPurchaseAndPlay")),
|
cancelPlay: () => dispatch(doSetPlayingUri(null)),
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
|
||||||
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(Video);
|
export default connect(select, perform)(Video);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import FilePrice from "component/filePrice";
|
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import Modal from "modal/modal";
|
|
||||||
|
|
||||||
class VideoPlayButton extends React.PureComponent {
|
class VideoPlayButton extends React.PureComponent {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -13,43 +11,22 @@ class VideoPlayButton extends React.PureComponent {
|
||||||
document.removeEventListener("keydown", this.keyDownListener);
|
document.removeEventListener("keydown", this.keyDownListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
onPurchaseConfirmed() {
|
|
||||||
this.props.closeModal();
|
|
||||||
this.props.startPlaying();
|
|
||||||
this.props.loadVideo(this.props.uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyDown(event) {
|
onKeyDown(event) {
|
||||||
if (
|
if (
|
||||||
"input" !== event.target.tagName.toLowerCase() &&
|
"input" !== event.target.tagName.toLowerCase() &&
|
||||||
"Space" === event.code
|
"Space" === event.code
|
||||||
) {
|
) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.onWatchClick();
|
this.watch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onWatchClick() {
|
watch() {
|
||||||
this.props.purchaseUri(this.props.uri).then(() => {
|
this.props.play(this.props.uri);
|
||||||
if (!this.props.modal) {
|
|
||||||
this.props.startPlaying();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { button, label, isLoading, fileInfo, mediaType } = this.props;
|
||||||
button,
|
|
||||||
label,
|
|
||||||
metadata,
|
|
||||||
metadata: { title },
|
|
||||||
uri,
|
|
||||||
modal,
|
|
||||||
closeModal,
|
|
||||||
isLoading,
|
|
||||||
fileInfo,
|
|
||||||
mediaType,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
title={
|
title={
|
||||||
|
@ -65,36 +42,14 @@ class VideoPlayButton extends React.PureComponent {
|
||||||
: "icon-folder-o";
|
: "icon-folder-o";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
|
||||||
<Link
|
<Link
|
||||||
button={button ? button : null}
|
button={button ? button : null}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label={label ? label : ""}
|
label={label ? label : ""}
|
||||||
className="video__play-button"
|
className="video__play-button"
|
||||||
icon={icon}
|
icon={icon}
|
||||||
onClick={this.onWatchClick.bind(this)}
|
onClick={() => this.watch()}
|
||||||
/>
|
/>
|
||||||
<Modal
|
|
||||||
type="confirm"
|
|
||||||
isOpen={modal == "affirmPurchaseAndPlay"}
|
|
||||||
contentLabel={__("Confirm Purchase")}
|
|
||||||
onConfirmed={this.onPurchaseConfirmed.bind(this)}
|
|
||||||
onAborted={closeModal}
|
|
||||||
>
|
|
||||||
{__("This will purchase")} <strong>{title}</strong> {__("for")}{" "}
|
|
||||||
<strong>
|
|
||||||
<FilePrice uri={uri} showFullPrice={true} look="plain" />
|
|
||||||
</strong>{" "}
|
|
||||||
{__("credits")}.
|
|
||||||
</Modal>
|
|
||||||
<Modal
|
|
||||||
isOpen={modal == "timedOut"}
|
|
||||||
onConfirmed={closeModal}
|
|
||||||
contentLabel={__("Timed Out")}
|
|
||||||
>
|
|
||||||
{__("Sorry, your download timed out :(")}
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,12 @@ class Video extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isPlaying: false,
|
|
||||||
showNsfwHelp: false,
|
showNsfwHelp: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillUnmount() {
|
||||||
// reset playing state upon change path action
|
this.props.cancelPlay();
|
||||||
if (
|
|
||||||
!this.isMediaSame(nextProps) &&
|
|
||||||
this.props.fileInfo &&
|
|
||||||
this.state.isPlaying
|
|
||||||
) {
|
|
||||||
this.state.isPlaying = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isMediaSame(nextProps) {
|
isMediaSame(nextProps) {
|
||||||
|
@ -33,12 +25,6 @@ class Video extends React.PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
startPlaying() {
|
|
||||||
this.setState({
|
|
||||||
isPlaying: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMouseOver() {
|
handleMouseOver() {
|
||||||
if (
|
if (
|
||||||
this.props.obscureNsfw &&
|
this.props.obscureNsfw &&
|
||||||
|
@ -64,13 +50,15 @@ class Video extends React.PureComponent {
|
||||||
metadata,
|
metadata,
|
||||||
isLoading,
|
isLoading,
|
||||||
isDownloading,
|
isDownloading,
|
||||||
|
playingUri,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
contentType,
|
contentType,
|
||||||
changeVolume,
|
changeVolume,
|
||||||
volume,
|
volume,
|
||||||
|
uri,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { isPlaying = false } = this.state;
|
|
||||||
|
|
||||||
|
const isPlaying = playingUri === uri;
|
||||||
const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0;
|
const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0;
|
||||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||||
const mediaType = lbry.getMediaType(
|
const mediaType = lbry.getMediaType(
|
||||||
|
@ -129,11 +117,7 @@ class Video extends React.PureComponent {
|
||||||
className="video__cover"
|
className="video__cover"
|
||||||
style={{ backgroundImage: 'url("' + metadata.thumbnail + '")' }}
|
style={{ backgroundImage: 'url("' + metadata.thumbnail + '")' }}
|
||||||
>
|
>
|
||||||
<VideoPlayButton
|
<VideoPlayButton {...this.props} mediaType={mediaType} />
|
||||||
startPlaying={this.startPlaying.bind(this)}
|
|
||||||
{...this.props}
|
|
||||||
mediaType={mediaType}
|
|
||||||
/>
|
|
||||||
</div>}
|
</div>}
|
||||||
{this.state.showNsfwHelp && <NsfwOverlay />}
|
{this.state.showNsfwHelp && <NsfwOverlay />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doCloseModal } from "actions/app";
|
|
||||||
import {
|
import {
|
||||||
doSendDraftTransaction,
|
doSendDraftTransaction,
|
||||||
doSetDraftTransactionAmount,
|
doSetDraftTransactionAmount,
|
||||||
doSetDraftTransactionAddress,
|
doSetDraftTransactionAddress,
|
||||||
} from "actions/wallet";
|
} from "actions/wallet";
|
||||||
import { selectCurrentModal } from "selectors/app";
|
|
||||||
import {
|
import {
|
||||||
selectDraftTransactionAmount,
|
selectDraftTransactionAmount,
|
||||||
selectDraftTransactionAddress,
|
selectDraftTransactionAddress,
|
||||||
|
@ -16,14 +14,12 @@ import {
|
||||||
import WalletSend from "./view";
|
import WalletSend from "./view";
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
modal: selectCurrentModal(state),
|
|
||||||
address: selectDraftTransactionAddress(state),
|
address: selectDraftTransactionAddress(state),
|
||||||
amount: selectDraftTransactionAmount(state),
|
amount: selectDraftTransactionAmount(state),
|
||||||
error: selectDraftTransactionError(state),
|
error: selectDraftTransactionError(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
|
||||||
sendToAddress: () => dispatch(doSendDraftTransaction()),
|
sendToAddress: () => dispatch(doSendDraftTransaction()),
|
||||||
setAmount: event => dispatch(doSetDraftTransactionAmount(event.target.value)),
|
setAmount: event => dispatch(doSetDraftTransactionAmount(event.target.value)),
|
||||||
setAddress: event =>
|
setAddress: event =>
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "component/link";
|
import { Form, FormRow, Submit } from "component/form";
|
||||||
import Modal from "modal/modal";
|
|
||||||
import { FormRow } from "component/form";
|
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
|
|
||||||
const WalletSend = props => {
|
class WalletSend extends React.PureComponent {
|
||||||
|
handleSubmit() {
|
||||||
|
const { amount, address, sendToAddress } = this.props;
|
||||||
|
const validSubmit = parseFloat(amount) > 0.0 && address;
|
||||||
|
|
||||||
|
if (validSubmit) {
|
||||||
|
sendToAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
const {
|
const {
|
||||||
sendToAddress,
|
|
||||||
closeModal,
|
closeModal,
|
||||||
modal,
|
modal,
|
||||||
setAmount,
|
setAmount,
|
||||||
|
@ -14,11 +21,11 @@ const WalletSend = props => {
|
||||||
amount,
|
amount,
|
||||||
address,
|
address,
|
||||||
error,
|
error,
|
||||||
} = props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<form onSubmit={sendToAddress}>
|
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||||
<div className="card__title-primary">
|
<div className="card__title-primary">
|
||||||
<h3>{__("Send Credits")}</h3>
|
<h3>{__("Send Credits")}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,44 +54,16 @@ const WalletSend = props => {
|
||||||
trim={true}
|
trim={true}
|
||||||
/>
|
/>
|
||||||
<div className="form-row-submit">
|
<div className="form-row-submit">
|
||||||
<Link
|
<Submit
|
||||||
button="primary"
|
|
||||||
label={__("Send")}
|
label={__("Send")}
|
||||||
onClick={sendToAddress}
|
|
||||||
disabled={!(parseFloat(amount) > 0.0) || !address}
|
disabled={!(parseFloat(amount) > 0.0) || !address}
|
||||||
/>
|
/>
|
||||||
<input type="submit" className="hidden" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</Form>
|
||||||
{modal == "insufficientBalance" &&
|
|
||||||
<Modal
|
|
||||||
isOpen={true}
|
|
||||||
contentLabel={__("Insufficient balance")}
|
|
||||||
onConfirmed={closeModal}
|
|
||||||
>
|
|
||||||
{__(
|
|
||||||
"Insufficient balance: after this transaction you would have less than 1 LBC in your wallet."
|
|
||||||
)}
|
|
||||||
</Modal>}
|
|
||||||
{modal == "transactionSuccessful" &&
|
|
||||||
<Modal
|
|
||||||
isOpen={true}
|
|
||||||
contentLabel={__("Transaction successful")}
|
|
||||||
onConfirmed={closeModal}
|
|
||||||
>
|
|
||||||
{__("Your transaction was successfully placed in the queue.")}
|
|
||||||
</Modal>}
|
|
||||||
{modal == "transactionFailed" &&
|
|
||||||
<Modal
|
|
||||||
isOpen={true}
|
|
||||||
contentLabel={__("Transaction failed")}
|
|
||||||
onConfirmed={closeModal}
|
|
||||||
>
|
|
||||||
{error}
|
|
||||||
</Modal>}
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default WalletSend;
|
export default WalletSend;
|
||||||
|
|
18
ui/js/component/walletSendTip/index.js
Normal file
18
ui/js/component/walletSendTip/index.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { doSendSupport } from "actions/wallet";
|
||||||
|
import WalletSendTip from "./view";
|
||||||
|
import { makeSelectTitleForUri } from "selectors/claims";
|
||||||
|
import { selectIsSendingSupport } from "selectors/wallet";
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
isPending: selectIsSendingSupport(state),
|
||||||
|
title: makeSelectTitleForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
sendSupport: (amount, claim_id, uri) =>
|
||||||
|
dispatch(doSendSupport(amount, claim_id, uri)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(WalletSendTip);
|
79
ui/js/component/walletSendTip/view.jsx
Normal file
79
ui/js/component/walletSendTip/view.jsx
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import React from "react";
|
||||||
|
import Link from "component/link";
|
||||||
|
import { FormRow } from "component/form";
|
||||||
|
import UriIndicator from "component/uriIndicator";
|
||||||
|
|
||||||
|
class WalletSendTip extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
amount: 0.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSendButtonClicked() {
|
||||||
|
const { claim_id, uri } = this.props;
|
||||||
|
let amount = this.state.amount;
|
||||||
|
this.props.sendSupport(amount, claim_id, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSupportPriceChange(event) {
|
||||||
|
this.setState({
|
||||||
|
amount: Number(event.target.value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { errorMessage, isPending, title, uri } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="card__title-primary">
|
||||||
|
<h1>{__("Support")} <UriIndicator uri={uri} /></h1>
|
||||||
|
</div>
|
||||||
|
<div className="card__content">
|
||||||
|
<FormRow
|
||||||
|
label={__("Amount")}
|
||||||
|
postfix={__("LBC")}
|
||||||
|
min="0"
|
||||||
|
step="0.1"
|
||||||
|
type="number"
|
||||||
|
errorMessage={errorMessage}
|
||||||
|
helper={
|
||||||
|
<span>
|
||||||
|
{__(
|
||||||
|
'This will appear as a tip for "%s" located at %s.',
|
||||||
|
title,
|
||||||
|
uri
|
||||||
|
) + " "}
|
||||||
|
<Link
|
||||||
|
label={__("Learn more")}
|
||||||
|
href="https://lbry.io/faq/tipping"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
placeholder="1.00"
|
||||||
|
onChange={event => this.handleSupportPriceChange(event)}
|
||||||
|
/>
|
||||||
|
<div className="form-row-submit">
|
||||||
|
<Link
|
||||||
|
label={__("Send")}
|
||||||
|
button="primary"
|
||||||
|
disabled={isPending}
|
||||||
|
onClick={this.handleSendButtonClicked.bind(this)}
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
label={__("Cancel")}
|
||||||
|
button="alt"
|
||||||
|
navigate="/show"
|
||||||
|
navigateParams={{ uri }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WalletSendTip;
|
|
@ -9,7 +9,6 @@ export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH";
|
||||||
export const VOLUME_CHANGED = "VOLUME_CHANGED";
|
export const VOLUME_CHANGED = "VOLUME_CHANGED";
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
export const CHANGE_PATH = "CHANGE_PATH";
|
|
||||||
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
|
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
|
||||||
export const WINDOW_SCROLLED = "WINDOW_SCROLLED";
|
export const WINDOW_SCROLLED = "WINDOW_SCROLLED";
|
||||||
export const HISTORY_NAVIGATE = "HISTORY_NAVIGATE";
|
export const HISTORY_NAVIGATE = "HISTORY_NAVIGATE";
|
||||||
|
@ -40,8 +39,11 @@ export const SEND_TRANSACTION_STARTED = "SEND_TRANSACTION_STARTED";
|
||||||
export const SEND_TRANSACTION_COMPLETED = "SEND_TRANSACTION_COMPLETED";
|
export const SEND_TRANSACTION_COMPLETED = "SEND_TRANSACTION_COMPLETED";
|
||||||
export const SEND_TRANSACTION_FAILED = "SEND_TRANSACTION_FAILED";
|
export const SEND_TRANSACTION_FAILED = "SEND_TRANSACTION_FAILED";
|
||||||
export const FETCH_BLOCK_SUCCESS = "FETCH_BLOCK_SUCCESS";
|
export const FETCH_BLOCK_SUCCESS = "FETCH_BLOCK_SUCCESS";
|
||||||
|
export const SUPPORT_TRANSACTION_STARTED = "SUPPORT_TRANSACTION_STARTED";
|
||||||
|
export const SUPPORT_TRANSACTION_COMPLETED = "SUPPORT_TRANSACTION_COMPLETED";
|
||||||
|
export const SUPPORT_TRANSACTION_FAILED = "SUPPORT_TRANSACTION_FAILED";
|
||||||
|
|
||||||
// Content
|
// Claims
|
||||||
export const FETCH_FEATURED_CONTENT_STARTED = "FETCH_FEATURED_CONTENT_STARTED";
|
export const FETCH_FEATURED_CONTENT_STARTED = "FETCH_FEATURED_CONTENT_STARTED";
|
||||||
export const FETCH_FEATURED_CONTENT_COMPLETED =
|
export const FETCH_FEATURED_CONTENT_COMPLETED =
|
||||||
"FETCH_FEATURED_CONTENT_COMPLETED";
|
"FETCH_FEATURED_CONTENT_COMPLETED";
|
||||||
|
@ -57,6 +59,20 @@ export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED =
|
||||||
export const FETCH_CLAIM_LIST_MINE_STARTED = "FETCH_CLAIM_LIST_MINE_STARTED";
|
export const FETCH_CLAIM_LIST_MINE_STARTED = "FETCH_CLAIM_LIST_MINE_STARTED";
|
||||||
export const FETCH_CLAIM_LIST_MINE_COMPLETED =
|
export const FETCH_CLAIM_LIST_MINE_COMPLETED =
|
||||||
"FETCH_CLAIM_LIST_MINE_COMPLETED";
|
"FETCH_CLAIM_LIST_MINE_COMPLETED";
|
||||||
|
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED";
|
||||||
|
export const ABANDON_CLAIM_SUCCEEDED = "ABANDON_CLAIM_SUCCEEDED";
|
||||||
|
export const FETCH_CHANNEL_LIST_MINE_STARTED =
|
||||||
|
"FETCH_CHANNEL_LIST_MINE_STARTED";
|
||||||
|
export const FETCH_CHANNEL_LIST_MINE_COMPLETED =
|
||||||
|
"FETCH_CHANNEL_LIST_MINE_COMPLETED";
|
||||||
|
export const CREATE_CHANNEL_STARTED = "CREATE_CHANNEL_STARTED";
|
||||||
|
export const CREATE_CHANNEL_COMPLETED = "CREATE_CHANNEL_COMPLETED";
|
||||||
|
export const PUBLISH_STARTED = "PUBLISH_STARTED";
|
||||||
|
export const PUBLISH_COMPLETED = "PUBLISH_COMPLETED";
|
||||||
|
export const PUBLISH_FAILED = "PUBLISH_FAILED";
|
||||||
|
export const SET_PLAYING_URI = "PLAY_URI";
|
||||||
|
|
||||||
|
// Files
|
||||||
export const FILE_LIST_STARTED = "FILE_LIST_STARTED";
|
export const FILE_LIST_STARTED = "FILE_LIST_STARTED";
|
||||||
export const FILE_LIST_SUCCEEDED = "FILE_LIST_SUCCEEDED";
|
export const FILE_LIST_SUCCEEDED = "FILE_LIST_SUCCEEDED";
|
||||||
export const FETCH_FILE_INFO_STARTED = "FETCH_FILE_INFO_STARTED";
|
export const FETCH_FILE_INFO_STARTED = "FETCH_FILE_INFO_STARTED";
|
||||||
|
@ -73,17 +89,6 @@ export const PLAY_VIDEO_STARTED = "PLAY_VIDEO_STARTED";
|
||||||
export const FETCH_AVAILABILITY_STARTED = "FETCH_AVAILABILITY_STARTED";
|
export const FETCH_AVAILABILITY_STARTED = "FETCH_AVAILABILITY_STARTED";
|
||||||
export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED";
|
export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED";
|
||||||
export const FILE_DELETE = "FILE_DELETE";
|
export const FILE_DELETE = "FILE_DELETE";
|
||||||
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED";
|
|
||||||
export const ABANDON_CLAIM_SUCCEEDED = "ABANDON_CLAIM_SUCCEEDED";
|
|
||||||
export const FETCH_CHANNEL_LIST_MINE_STARTED =
|
|
||||||
"FETCH_CHANNEL_LIST_MINE_STARTED";
|
|
||||||
export const FETCH_CHANNEL_LIST_MINE_COMPLETED =
|
|
||||||
"FETCH_CHANNEL_LIST_MINE_COMPLETED";
|
|
||||||
export const CREATE_CHANNEL_STARTED = "CREATE_CHANNEL_STARTED";
|
|
||||||
export const CREATE_CHANNEL_COMPLETED = "CREATE_CHANNEL_COMPLETED";
|
|
||||||
export const PUBLISH_STARTED = "PUBLISH_STARTED";
|
|
||||||
export const PUBLISH_COMPLETED = "PUBLISH_COMPLETED";
|
|
||||||
export const PUBLISH_FAILED = "PUBLISH_FAILED";
|
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
export const SEARCH_STARTED = "SEARCH_STARTED";
|
export const SEARCH_STARTED = "SEARCH_STARTED";
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export const CONFIRM_FILE_REMOVE = "confirmFileRemove";
|
export const CONFIRM_FILE_REMOVE = "confirmFileRemove";
|
||||||
export const INCOMPATIBLE_DAEMON = "incompatibleDaemon";
|
export const INCOMPATIBLE_DAEMON = "incompatibleDaemon";
|
||||||
|
export const FILE_TIMEOUT = "file_timeout";
|
||||||
export const DOWNLOADING = "downloading";
|
export const DOWNLOADING = "downloading";
|
||||||
export const ERROR = "error";
|
export const ERROR = "error";
|
||||||
export const INSUFFICIENT_CREDITS = "insufficient_credits";
|
export const INSUFFICIENT_CREDITS = "insufficient_credits";
|
||||||
|
@ -7,5 +8,8 @@ export const UPGRADE = "upgrade";
|
||||||
export const WELCOME = "welcome";
|
export const WELCOME = "welcome";
|
||||||
export const FIRST_REWARD = "first_reward";
|
export const FIRST_REWARD = "first_reward";
|
||||||
export const AUTHENTICATION_FAILURE = "auth_failure";
|
export const AUTHENTICATION_FAILURE = "auth_failure";
|
||||||
|
export const TRANSACTION_FAILED = "transaction_failed";
|
||||||
|
export const INSUFFICIENT_BALANCE = "insufficient_balance";
|
||||||
export const REWARD_APPROVAL_REQUIRED = "reward_approval_required";
|
export const REWARD_APPROVAL_REQUIRED = "reward_approval_required";
|
||||||
|
export const AFFIRM_PURCHASE = "affirm_purchase";
|
||||||
export const CREDIT_INTRO = "credit_intro";
|
export const CREDIT_INTRO = "credit_intro";
|
||||||
|
|
|
@ -6,3 +6,5 @@ export const NEW_USER_ACKNOWLEDGED = "welcome_acknowledged";
|
||||||
export const LANGUAGE = "language";
|
export const LANGUAGE = "language";
|
||||||
export const SHOW_NSFW = "showNsfw";
|
export const SHOW_NSFW = "showNsfw";
|
||||||
export const SHOW_UNAVAILABLE = "showUnavailable";
|
export const SHOW_UNAVAILABLE = "showUnavailable";
|
||||||
|
export const THEME = "theme";
|
||||||
|
export const THEMES = "themes";
|
||||||
|
|
|
@ -39,6 +39,8 @@ let lbry = {
|
||||||
customLighthouseServers: [],
|
customLighthouseServers: [],
|
||||||
showDeveloperMenu: false,
|
showDeveloperMenu: false,
|
||||||
language: "en",
|
language: "en",
|
||||||
|
theme: "light",
|
||||||
|
themes: [],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -174,60 +176,6 @@ lbry.connect = function() {
|
||||||
return lbry._connectPromise;
|
return lbry._connectPromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes a LBRY URI; will first try and calculate a total cost using
|
|
||||||
* Lighthouse. If Lighthouse can't be reached, it just retrives the
|
|
||||||
* key fee.
|
|
||||||
*
|
|
||||||
* Returns an object with members:
|
|
||||||
* - cost: Number; the calculated cost of the name
|
|
||||||
* - includes_data: Boolean; indicates whether or not the data fee info
|
|
||||||
* from Lighthouse is included.
|
|
||||||
*/
|
|
||||||
lbry.costPromiseCache = {};
|
|
||||||
lbry.getCostInfo = function(uri) {
|
|
||||||
if (lbry.costPromiseCache[uri] === undefined) {
|
|
||||||
lbry.costPromiseCache[uri] = new Promise((resolve, reject) => {
|
|
||||||
const COST_INFO_CACHE_KEY = "cost_info_cache";
|
|
||||||
let costInfoCache = getSession(COST_INFO_CACHE_KEY, {});
|
|
||||||
|
|
||||||
function cacheAndResolve(cost, includesData) {
|
|
||||||
costInfoCache[uri] = { cost, includesData };
|
|
||||||
setSession(COST_INFO_CACHE_KEY, costInfoCache);
|
|
||||||
resolve({ cost, includesData });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!uri) {
|
|
||||||
return reject(new Error(`URI required.`));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (costInfoCache[uri] && costInfoCache[uri].cost) {
|
|
||||||
return resolve(costInfoCache[uri]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCost(uri, size) {
|
|
||||||
lbry
|
|
||||||
.stream_cost_estimate({ uri, ...(size !== null ? { size } : {}) })
|
|
||||||
.then(cost => {
|
|
||||||
cacheAndResolve(cost, size !== null);
|
|
||||||
}, reject);
|
|
||||||
}
|
|
||||||
|
|
||||||
const uriObj = lbryuri.parse(uri);
|
|
||||||
const name = uriObj.path || uriObj.name;
|
|
||||||
|
|
||||||
lighthouse.get_size_for_name(name).then(size => {
|
|
||||||
if (size) {
|
|
||||||
getCost(name, size);
|
|
||||||
} else {
|
|
||||||
getCost(name, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return lbry.costPromiseCache[uri];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publishes a file. The optional fileListedCallback is called when the file becomes available in
|
* Publishes a file. The optional fileListedCallback is called when the file becomes available in
|
||||||
* lbry.file_list() during the publish process.
|
* lbry.file_list() during the publish process.
|
||||||
|
@ -277,17 +225,6 @@ lbry.publishDeprecated = function(
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
lbry.getClientSettings = function() {
|
|
||||||
var outSettings = {};
|
|
||||||
for (let setting of Object.keys(lbry.defaultClientSettings)) {
|
|
||||||
var localStorageVal = localStorage.getItem("setting_" + setting);
|
|
||||||
outSettings[setting] = localStorageVal === null
|
|
||||||
? lbry.defaultClientSettings[setting]
|
|
||||||
: JSON.parse(localStorageVal);
|
|
||||||
}
|
|
||||||
return outSettings;
|
|
||||||
};
|
|
||||||
|
|
||||||
lbry.getClientSetting = function(setting) {
|
lbry.getClientSetting = function(setting) {
|
||||||
var localStorageVal = localStorage.getItem("setting_" + setting);
|
var localStorageVal = localStorage.getItem("setting_" + setting);
|
||||||
if (setting == "showDeveloperMenu") {
|
if (setting == "showDeveloperMenu") {
|
||||||
|
@ -298,12 +235,6 @@ lbry.getClientSetting = function(setting) {
|
||||||
: JSON.parse(localStorageVal);
|
: JSON.parse(localStorageVal);
|
||||||
};
|
};
|
||||||
|
|
||||||
lbry.setClientSettings = function(settings) {
|
|
||||||
for (let setting of Object.keys(settings)) {
|
|
||||||
lbry.setClientSetting(setting, settings[setting]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
lbry.setClientSetting = function(setting, value) {
|
lbry.setClientSetting = function(setting, value) {
|
||||||
return localStorage.setItem("setting_" + setting, JSON.stringify(value));
|
return localStorage.setItem("setting_" + setting, JSON.stringify(value));
|
||||||
};
|
};
|
||||||
|
|
21
ui/js/modal/modalAffirmPurchase/index.js
Normal file
21
ui/js/modal/modalAffirmPurchase/index.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { doCloseModal } from "actions/app";
|
||||||
|
import { doLoadVideo, doSetPlayingUri } from "actions/content";
|
||||||
|
import { makeSelectMetadataForUri } from "selectors/claims";
|
||||||
|
import ModalAffirmPurchase from "./view";
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
cancelPurchase: () => {
|
||||||
|
dispatch(doSetPlayingUri(null));
|
||||||
|
dispatch(doCloseModal());
|
||||||
|
},
|
||||||
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
|
loadVideo: uri => dispatch(doLoadVideo(uri)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(ModalAffirmPurchase);
|
32
ui/js/modal/modalAffirmPurchase/view.jsx
Normal file
32
ui/js/modal/modalAffirmPurchase/view.jsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import React from "react";
|
||||||
|
import FilePrice from "component/filePrice";
|
||||||
|
import { Modal } from "modal/modal";
|
||||||
|
|
||||||
|
class ModalAffirmPurchase extends React.PureComponent {
|
||||||
|
onAffirmPurchase() {
|
||||||
|
this.props.closeModal();
|
||||||
|
this.props.loadVideo(this.props.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { cancelPurchase, metadata: { title }, uri } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
type="confirm"
|
||||||
|
isOpen={true}
|
||||||
|
contentLabel={__("Confirm Purchase")}
|
||||||
|
onConfirmed={this.onAffirmPurchase.bind(this)}
|
||||||
|
onAborted={cancelPurchase}
|
||||||
|
>
|
||||||
|
{__("This will purchase")} <strong>{title}</strong> {__("for")}{" "}
|
||||||
|
<strong>
|
||||||
|
<FilePrice uri={uri} showFullPrice={true} look="plain" />
|
||||||
|
</strong>{" "}
|
||||||
|
{__("credits")}.
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalAffirmPurchase;
|
|
@ -9,4 +9,4 @@ const perform = dispatch => ({
|
||||||
close: () => dispatch(doCloseModal()),
|
close: () => dispatch(doCloseModal()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(ModalAuthFailure);
|
export default connect(null, null)(ModalAuthFailure);
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { selectCurrentModal, selectModalExtraContent } from "selectors/app";
|
|
||||||
import { doCloseModal } from "actions/app";
|
import { doCloseModal } from "actions/app";
|
||||||
import ModalError from "./view";
|
import ModalError from "./view";
|
||||||
|
|
||||||
const select = state => ({
|
|
||||||
modal: selectCurrentModal(state),
|
|
||||||
error: selectModalExtraContent(state),
|
|
||||||
});
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(ModalError);
|
export default connect(null, perform)(ModalError);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ExpandableModal } from "modal/modal";
|
||||||
|
|
||||||
class ModalError extends React.PureComponent {
|
class ModalError extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { modal, closeModal, error } = this.props;
|
const { closeModal, error } = this.props;
|
||||||
|
|
||||||
const errorObj = typeof error === "string" ? { message: error } : error;
|
const errorObj = typeof error === "string" ? { message: error } : error;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class ModalError extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExpandableModal
|
<ExpandableModal
|
||||||
isOpen={modal == "error"}
|
isOpen={true}
|
||||||
contentLabel={__("Error")}
|
contentLabel={__("Error")}
|
||||||
className="error-modal"
|
className="error-modal"
|
||||||
overlayClassName="error-modal-overlay"
|
overlayClassName="error-modal-overlay"
|
||||||
|
|
15
ui/js/modal/modalFileTimeout/index.js
Normal file
15
ui/js/modal/modalFileTimeout/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { doCloseModal } from "actions/app";
|
||||||
|
import { makeSelectMetadataForUri } from "selectors/claims";
|
||||||
|
import ModalFileTimeout from "./view";
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(ModalFileTimeout);
|
21
ui/js/modal/modalFileTimeout/view.jsx
Normal file
21
ui/js/modal/modalFileTimeout/view.jsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Modal } from "modal/modal";
|
||||||
|
|
||||||
|
class ModalFileTimeout extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const { metadata: { title } } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={true}
|
||||||
|
contentLabel={__("Download failed")}
|
||||||
|
onConfirmed={closeModal}
|
||||||
|
>
|
||||||
|
{__("LBRY was unable to download the stream")}{" "}
|
||||||
|
<strong>{title}</strong>.
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalFileTimeout;
|
17
ui/js/modal/modalInsufficientBalance/index.js
Normal file
17
ui/js/modal/modalInsufficientBalance/index.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { doCloseModal } from "actions/app";
|
||||||
|
import { doNavigate } from "actions/navigation";
|
||||||
|
import ModalInsufficientBalance from "./view";
|
||||||
|
|
||||||
|
const select = state => ({});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
addBalance: () => {
|
||||||
|
dispatch(doNavigate("/wallet"));
|
||||||
|
dispatch(doCloseModal());
|
||||||
|
},
|
||||||
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(ModalInsufficientBalance);
|
26
ui/js/modal/modalInsufficientBalance/view.jsx
Normal file
26
ui/js/modal/modalInsufficientBalance/view.jsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Modal } from "modal/modal";
|
||||||
|
|
||||||
|
class ModalInsufficientBalance extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const { addBalance, closeModal } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={true}
|
||||||
|
type="confirm"
|
||||||
|
contentLabel={__("Not enough credits")}
|
||||||
|
confirmButtonLabel={__("Get Credits")}
|
||||||
|
abortButtonLabel={__("Cancel")}
|
||||||
|
onAborted={closeModal}
|
||||||
|
onConfirmed={addBalance}
|
||||||
|
>
|
||||||
|
{__(
|
||||||
|
"Insufficient balance: after this transaction you would have less than 0 LBCs in your wallet."
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalInsufficientBalance;
|
|
@ -2,19 +2,15 @@ import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doCloseModal } from "actions/app";
|
import { doCloseModal } from "actions/app";
|
||||||
import { doDeleteFileAndGoBack } from "actions/file_info";
|
import { doDeleteFileAndGoBack } from "actions/file_info";
|
||||||
import { makeSelectClaimForUriIsMine } from "selectors/claims";
|
import { makeSelectTitleForUri, makeSelectClaimIsMine } from "selectors/claims";
|
||||||
|
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
||||||
import ModalRemoveFile from "./view";
|
import ModalRemoveFile from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectClaimForUriIsMine = makeSelectClaimForUriIsMine();
|
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
|
title: makeSelectTitleForUri(props.uri)(state),
|
||||||
const select = (state, props) => ({
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
claimIsMine: selectClaimForUriIsMine(state, props),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
|
@ -23,4 +19,4 @@ const perform = dispatch => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(ModalRemoveFile);
|
export default connect(select, perform)(ModalRemoveFile);
|
||||||
|
|
|
@ -25,7 +25,13 @@ class ModalRemoveFile extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { claimIsMine, closeModal, deleteFile, outpoint, title } = this.props;
|
const {
|
||||||
|
claimIsMine,
|
||||||
|
closeModal,
|
||||||
|
deleteFile,
|
||||||
|
fileInfo: { outpoint },
|
||||||
|
title,
|
||||||
|
} = this.props;
|
||||||
const { deleteChecked, abandonClaimChecked } = this.state;
|
const { deleteChecked, abandonClaimChecked } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doOpenModal } from "actions/app";
|
import { doOpenModal } from "actions/app";
|
||||||
import * as settings from "constants/settings";
|
import * as settings from "constants/settings";
|
||||||
import { selectCurrentModal } from "selectors/app";
|
import { selectCurrentModal, selectModalProps } from "selectors/app";
|
||||||
import { selectCurrentPage } from "selectors/navigation";
|
import { selectCurrentPage } from "selectors/navigation";
|
||||||
import { selectCostForCurrentPageUri } from "selectors/cost_info";
|
import { selectCostForCurrentPageUri } from "selectors/cost_info";
|
||||||
import { makeSelectClientSetting } from "selectors/settings";
|
import { makeSelectClientSetting } from "selectors/settings";
|
||||||
|
@ -14,6 +14,7 @@ const select = (state, props) => ({
|
||||||
balance: selectBalance(state),
|
balance: selectBalance(state),
|
||||||
showPageCost: selectCostForCurrentPageUri(state),
|
showPageCost: selectCostForCurrentPageUri(state),
|
||||||
modal: selectCurrentModal(state),
|
modal: selectCurrentModal(state),
|
||||||
|
modalProps: selectModalProps(state),
|
||||||
page: selectCurrentPage(state),
|
page: selectCurrentPage(state),
|
||||||
isWelcomeAcknowledged: makeSelectClientSetting(
|
isWelcomeAcknowledged: makeSelectClientSetting(
|
||||||
settings.NEW_USER_ACKNOWLEDGED
|
settings.NEW_USER_ACKNOWLEDGED
|
||||||
|
|
|
@ -7,8 +7,13 @@ import ModalUpgrade from "modal/modalUpgrade";
|
||||||
import ModalWelcome from "modal/modalWelcome";
|
import ModalWelcome from "modal/modalWelcome";
|
||||||
import ModalFirstReward from "modal/modalFirstReward";
|
import ModalFirstReward from "modal/modalFirstReward";
|
||||||
import ModalRewardApprovalRequired from "modal/modalRewardApprovalRequired";
|
import ModalRewardApprovalRequired from "modal/modalRewardApprovalRequired";
|
||||||
import * as modals from "constants/modal_types";
|
|
||||||
import ModalCreditIntro from "modal/modalCreditIntro";
|
import ModalCreditIntro from "modal/modalCreditIntro";
|
||||||
|
import ModalRemoveFile from "modal/modalRemoveFile";
|
||||||
|
import ModalTransactionFailed from "modal/modalTransactionFailed";
|
||||||
|
import ModalInsufficientBalance from "modal/modalInsufficientBalance";
|
||||||
|
import ModalFileTimeout from "modal/modalFileTimeout";
|
||||||
|
import ModalAffirmPurchase from "modal/modalAffirmPurchase";
|
||||||
|
import * as modals from "constants/modal_types";
|
||||||
|
|
||||||
class ModalRouter extends React.PureComponent {
|
class ModalRouter extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -29,7 +34,7 @@ class ModalRouter extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
showTransitionModals(props) {
|
showTransitionModals(props) {
|
||||||
const { modal, openModal, page } = props;
|
const { modal, modalProps, openModal, page } = props;
|
||||||
|
|
||||||
if (modal) {
|
if (modal) {
|
||||||
return;
|
return;
|
||||||
|
@ -96,27 +101,37 @@ class ModalRouter extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { modal } = this.props;
|
const { modal, modalProps } = this.props;
|
||||||
|
|
||||||
switch (modal) {
|
switch (modal) {
|
||||||
case modals.UPGRADE:
|
case modals.UPGRADE:
|
||||||
return <ModalUpgrade />;
|
return <ModalUpgrade {...modalProps} />;
|
||||||
case modals.DOWNLOADING:
|
case modals.DOWNLOADING:
|
||||||
return <ModalDownloading />;
|
return <ModalDownloading {...modalProps} />;
|
||||||
case modals.ERROR:
|
case modals.ERROR:
|
||||||
return <ModalError />;
|
return <ModalError {...modalProps} />;
|
||||||
|
case modals.FILE_TIMEOUT:
|
||||||
|
return <ModalFileTimeout {...modalProps} />;
|
||||||
case modals.INSUFFICIENT_CREDITS:
|
case modals.INSUFFICIENT_CREDITS:
|
||||||
return <ModalInsufficientCredits />;
|
return <ModalInsufficientCredits {...modalProps} />;
|
||||||
case modals.WELCOME:
|
case modals.WELCOME:
|
||||||
return <ModalWelcome />;
|
return <ModalWelcome {...modalProps} />;
|
||||||
case modals.FIRST_REWARD:
|
case modals.FIRST_REWARD:
|
||||||
return <ModalFirstReward />;
|
return <ModalFirstReward {...modalProps} />;
|
||||||
case modals.AUTHENTICATION_FAILURE:
|
case modals.AUTHENTICATION_FAILURE:
|
||||||
return <ModalAuthFailure />;
|
return <ModalAuthFailure {...modalProps} />;
|
||||||
case modals.CREDIT_INTRO:
|
case modals.CREDIT_INTRO:
|
||||||
return <ModalCreditIntro />;
|
return <ModalCreditIntro {...modalProps} />;
|
||||||
|
case modals.TRANSACTION_FAILED:
|
||||||
|
return <ModalTransactionFailed {...modalProps} />;
|
||||||
|
case modals.INSUFFICIENT_BALANCE:
|
||||||
|
return <ModalInsufficientBalance {...modalProps} />;
|
||||||
case modals.REWARD_APPROVAL_REQUIRED:
|
case modals.REWARD_APPROVAL_REQUIRED:
|
||||||
return <ModalRewardApprovalRequired />;
|
return <ModalRewardApprovalRequired {...modalProps} />;
|
||||||
|
case modals.CONFIRM_FILE_REMOVE:
|
||||||
|
return <ModalRemoveFile {...modalProps} />;
|
||||||
|
case modals.AFFIRM_PURCHASE:
|
||||||
|
return <ModalAffirmPurchase {...modalProps} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
12
ui/js/modal/modalTransactionFailed/index.js
Normal file
12
ui/js/modal/modalTransactionFailed/index.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { doCloseModal } from "actions/app";
|
||||||
|
import ModalTransactionFailed from "./view";
|
||||||
|
|
||||||
|
const select = state => ({});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(ModalTransactionFailed);
|
20
ui/js/modal/modalTransactionFailed/view.jsx
Normal file
20
ui/js/modal/modalTransactionFailed/view.jsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Modal } from "modal/modal";
|
||||||
|
|
||||||
|
class ModalTransactionFailed extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const { closeModal } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={true}
|
||||||
|
contentLabel={__("Transaction failed")}
|
||||||
|
onConfirmed={closeModal}
|
||||||
|
>
|
||||||
|
{__("Something went wrong")}:
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalTransactionFailed;
|
|
@ -9,27 +9,22 @@ import {
|
||||||
makeSelectClaimsInChannelForCurrentPage,
|
makeSelectClaimsInChannelForCurrentPage,
|
||||||
makeSelectFetchingChannelClaims,
|
makeSelectFetchingChannelClaims,
|
||||||
} from "selectors/claims";
|
} from "selectors/claims";
|
||||||
import { selectCurrentParams } from "selectors/navigation";
|
import {
|
||||||
|
makeSelectCurrentParam,
|
||||||
|
selectCurrentParams,
|
||||||
|
} from "selectors/navigation";
|
||||||
import { doNavigate } from "actions/navigation";
|
import { doNavigate } from "actions/navigation";
|
||||||
import { makeSelectTotalPagesForChannel } from "selectors/content";
|
import { makeSelectTotalPagesForChannel } from "selectors/content";
|
||||||
import ChannelPage from "./view";
|
import ChannelPage from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectClaim = makeSelectClaimForUri(),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
selectClaimsInChannel = makeSelectClaimsInChannelForCurrentPage(),
|
claimsInChannel: makeSelectClaimsInChannelForCurrentPage(props.uri)(state),
|
||||||
selectFetchingChannelClaims = makeSelectFetchingChannelClaims(),
|
fetching: makeSelectFetchingChannelClaims(props.uri)(state),
|
||||||
selectTotalPagesForChannel = makeSelectTotalPagesForChannel();
|
page: makeSelectCurrentParam("page")(state),
|
||||||
|
|
||||||
const select = (state, props) => ({
|
|
||||||
claim: selectClaim(state, props),
|
|
||||||
claimsInChannel: selectClaimsInChannel(state, props),
|
|
||||||
fetching: selectFetchingChannelClaims(state, props),
|
|
||||||
totalPages: selectTotalPagesForChannel(state, props),
|
|
||||||
params: selectCurrentParams(state),
|
params: selectCurrentParams(state),
|
||||||
});
|
totalPages: makeSelectTotalPagesForChannel(props.uri)(state),
|
||||||
|
});
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
|
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
|
||||||
|
@ -37,4 +32,4 @@ const perform = dispatch => ({
|
||||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(ChannelPage);
|
export default connect(select, perform)(ChannelPage);
|
||||||
|
|
|
@ -2,31 +2,29 @@ import React from "react";
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
import { BusyMessage } from "component/common";
|
import { BusyMessage } from "component/common";
|
||||||
import FileTile from "component/fileTile";
|
import FileTile from "component/fileTile";
|
||||||
import Link from "component/link";
|
|
||||||
import ReactPaginate from "react-paginate";
|
import ReactPaginate from "react-paginate";
|
||||||
|
|
||||||
class ChannelPage extends React.PureComponent {
|
class ChannelPage extends React.PureComponent {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { uri, params, fetchClaims, fetchClaimCount } = this.props;
|
const { uri, page, fetchClaims, fetchClaimCount } = this.props;
|
||||||
|
|
||||||
fetchClaims(uri, params.page || 1);
|
fetchClaims(uri, page || 1);
|
||||||
fetchClaimCount(uri);
|
fetchClaimCount(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
const { params, fetching, fetchClaims, fetchClaimCount } = this.props;
|
const { page, uri, fetching, fetchClaims, fetchClaimCount } = this.props;
|
||||||
const nextParams = nextProps.params;
|
|
||||||
|
|
||||||
if (fetching !== nextParams.page && params.page !== nextParams.page) {
|
if (nextProps.page && page !== nextProps.page) {
|
||||||
fetchClaims(nextProps.uri, nextParams.page);
|
fetchClaims(nextProps.uri, nextProps.page);
|
||||||
}
|
}
|
||||||
if (nextProps.uri != this.props.uri) {
|
if (nextProps.uri != uri) {
|
||||||
fetchClaimCount(uri);
|
fetchClaimCount(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changePage(pageNumber) {
|
changePage(pageNumber) {
|
||||||
const { params, currentPage } = this.props;
|
const { params } = this.props;
|
||||||
const newParams = Object.assign({}, params, { page: pageNumber });
|
const newParams = Object.assign({}, params, { page: pageNumber });
|
||||||
|
|
||||||
this.props.navigate("/show", newParams);
|
this.props.navigate("/show", newParams);
|
||||||
|
@ -38,16 +36,15 @@ class ChannelPage extends React.PureComponent {
|
||||||
claimsInChannel,
|
claimsInChannel,
|
||||||
claim,
|
claim,
|
||||||
uri,
|
uri,
|
||||||
params,
|
page,
|
||||||
totalPages,
|
totalPages,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { page } = params;
|
|
||||||
|
|
||||||
let contentList;
|
let contentList;
|
||||||
if (claimsInChannel === undefined) {
|
if (fetching) {
|
||||||
contentList = <BusyMessage message={__("Fetching content")} />;
|
contentList = <BusyMessage message={__("Fetching content")} />;
|
||||||
} else if (claimsInChannel) {
|
} else {
|
||||||
contentList = claimsInChannel.length
|
contentList = claimsInChannel && claimsInChannel.length
|
||||||
? claimsInChannel.map(claim =>
|
? claimsInChannel.map(claim =>
|
||||||
<FileTile
|
<FileTile
|
||||||
key={claim.claim_id}
|
key={claim.claim_id}
|
||||||
|
|
|
@ -215,11 +215,7 @@ class DiscoverPage extends React.PureComponent {
|
||||||
failedToLoad = !fetchingFeaturedUris && !hasContent;
|
failedToLoad = !fetchingFeaturedUris && !hasContent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main
|
<main className={hasContent && fetchingFeaturedUris ? "reloading" : null}>
|
||||||
className={
|
|
||||||
hasContent && fetchingFeaturedUris ? "main--refreshing" : null
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{!hasContent &&
|
{!hasContent &&
|
||||||
fetchingFeaturedUris &&
|
fetchingFeaturedUris &&
|
||||||
<BusyMessage message={__("Fetching content")} />}
|
<BusyMessage message={__("Fetching content")} />}
|
||||||
|
|
|
@ -13,26 +13,18 @@ import {
|
||||||
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
||||||
import { selectShowNsfw } from "selectors/settings";
|
import { selectShowNsfw } from "selectors/settings";
|
||||||
import FilePage from "./view";
|
import FilePage from "./view";
|
||||||
|
import { makeSelectCurrentParam } from "selectors/navigation";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectClaim = makeSelectClaimForUri(),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
selectContentType = makeSelectContentTypeForUri(),
|
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
||||||
selectFileInfo = makeSelectFileInfoForUri(),
|
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||||
selectCostInfo = makeSelectCostInfoForUri(),
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
selectMetadata = makeSelectMetadataForUri();
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
|
||||||
claim: selectClaim(state, props),
|
|
||||||
contentType: selectContentType(state, props),
|
|
||||||
costInfo: selectCostInfo(state, props),
|
|
||||||
metadata: selectMetadata(state, props),
|
|
||||||
obscureNsfw: !selectShowNsfw(state),
|
obscureNsfw: !selectShowNsfw(state),
|
||||||
fileInfo: selectFileInfo(state, props),
|
tab: makeSelectCurrentParam("tab")(state),
|
||||||
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
||||||
});
|
});
|
||||||
|
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
|
@ -40,4 +32,4 @@ const perform = dispatch => ({
|
||||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(FilePage);
|
export default connect(select, perform)(FilePage);
|
||||||
|
|
|
@ -1,45 +1,13 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactMarkdown from "react-markdown";
|
|
||||||
import lbry from "lbry.js";
|
import lbry from "lbry.js";
|
||||||
import lbryuri from "lbryuri.js";
|
import lbryuri from "lbryuri.js";
|
||||||
import Video from "component/video";
|
import Video from "component/video";
|
||||||
import { Thumbnail } from "component/common";
|
import { Thumbnail } from "component/common";
|
||||||
import FilePrice from "component/filePrice";
|
import FilePrice from "component/filePrice";
|
||||||
import FileActions from "component/fileActions";
|
import FileDetails from "component/fileDetails";
|
||||||
import Link from "component/link";
|
|
||||||
import UriIndicator from "component/uriIndicator";
|
import UriIndicator from "component/uriIndicator";
|
||||||
import IconFeatured from "component/iconFeatured";
|
import IconFeatured from "component/iconFeatured";
|
||||||
import DateTime from "component/dateTime";
|
import WalletSendTip from "component/walletSendTip";
|
||||||
|
|
||||||
const FormatItem = props => {
|
|
||||||
const {
|
|
||||||
publishedDate,
|
|
||||||
contentType,
|
|
||||||
claim: { height },
|
|
||||||
metadata: { language, license },
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const mediaType = lbry.getMediaType(contentType);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<table className="table-standard">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>{__("Published on")}</td><td><DateTime block={height} /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{__("Content-Type")}</td><td>{mediaType}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{__("Language")}</td><td>{language}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{__("License")}</td><td>{license}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
class FilePage extends React.PureComponent {
|
class FilePage extends React.PureComponent {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -69,34 +37,20 @@ class FilePage extends React.PureComponent {
|
||||||
fileInfo,
|
fileInfo,
|
||||||
metadata,
|
metadata,
|
||||||
contentType,
|
contentType,
|
||||||
|
tab,
|
||||||
uri,
|
uri,
|
||||||
rewardedContentClaimIds,
|
rewardedContentClaimIds,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const showTipBox = tab == "tip";
|
||||||
|
|
||||||
if (!claim || !metadata) {
|
if (!claim || !metadata) {
|
||||||
return (
|
return (
|
||||||
<span className="empty">{__("Empty claim or metadata info.")}</span>
|
<span className="empty">{__("Empty claim or metadata info.")}</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
|
||||||
txid,
|
|
||||||
nout,
|
|
||||||
channel_name: channelName,
|
|
||||||
has_signature: hasSignature,
|
|
||||||
signature_is_valid: signatureIsValid,
|
|
||||||
value,
|
|
||||||
} = claim;
|
|
||||||
|
|
||||||
const outpoint = txid + ":" + nout;
|
|
||||||
const title = metadata.title;
|
const title = metadata.title;
|
||||||
const channelClaimId = claim.value && claim.value.publisherSignature
|
|
||||||
? claim.value.publisherSignature.certificateId
|
|
||||||
: null;
|
|
||||||
const channelUri = signatureIsValid && hasSignature && channelName
|
|
||||||
? lbryuri.build({ channelName, claimId: channelClaimId }, false)
|
|
||||||
: null;
|
|
||||||
const uriIndicator = <UriIndicator uri={uri} />;
|
|
||||||
const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id);
|
const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id);
|
||||||
const mediaType = lbry.getMediaType(contentType);
|
const mediaType = lbry.getMediaType(contentType);
|
||||||
const player = require("render-media");
|
const player = require("render-media");
|
||||||
|
@ -106,7 +60,7 @@ class FilePage extends React.PureComponent {
|
||||||
mediaType === "audio";
|
mediaType === "audio";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<div>
|
||||||
<section className="show-page-media">
|
<section className="show-page-media">
|
||||||
{isPlayable
|
{isPlayable
|
||||||
? <Video className="video-embedded" uri={uri} />
|
? <Video className="video-embedded" uri={uri} />
|
||||||
|
@ -116,6 +70,9 @@ class FilePage extends React.PureComponent {
|
||||||
</section>
|
</section>
|
||||||
<section className={"card " + (obscureNsfw ? "card--obscured " : "")}>
|
<section className={"card " + (obscureNsfw ? "card--obscured " : "")}>
|
||||||
<div className="card__inner">
|
<div className="card__inner">
|
||||||
|
{(!tab || tab === "details") &&
|
||||||
|
<div>
|
||||||
|
{" "} {" "}
|
||||||
<div className="card__title-identity">
|
<div className="card__title-identity">
|
||||||
{!fileInfo || fileInfo.written_bytes <= 0
|
{!fileInfo || fileInfo.written_bytes <= 0
|
||||||
? <span style={{ float: "right" }}>
|
? <span style={{ float: "right" }}>
|
||||||
|
@ -125,45 +82,16 @@ class FilePage extends React.PureComponent {
|
||||||
: null}
|
: null}
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
{channelUri
|
<UriIndicator uri={uri} link={true} />
|
||||||
? <Link
|
|
||||||
onClick={() =>
|
|
||||||
this.props.navigate("/show", { uri: channelUri })}
|
|
||||||
>
|
|
||||||
{uriIndicator}
|
|
||||||
</Link>
|
|
||||||
: uriIndicator}
|
|
||||||
</div>
|
|
||||||
<div className="card__actions">
|
|
||||||
<FileActions uri={uri} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
<FileDetails uri={uri} />
|
||||||
<ReactMarkdown
|
</div>}
|
||||||
source={(metadata && metadata.description) || ""}
|
{tab === "tip" &&
|
||||||
escapeHtml={true}
|
<WalletSendTip claim_id={claim.claim_id} uri={uri} />}
|
||||||
disallowedTypes={["Heading", "HtmlInline", "HtmlBlock"]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{metadata && claim
|
|
||||||
? <div className="card__content">
|
|
||||||
<FormatItem
|
|
||||||
metadata={metadata}
|
|
||||||
contentType={contentType}
|
|
||||||
claim={claim}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
: ""}
|
|
||||||
<div className="card__content">
|
|
||||||
<Link
|
|
||||||
href={`https://lbry.io/dmca?claim_id=${claim.claim_id}`}
|
|
||||||
label={__("report")}
|
|
||||||
className="button-text-help"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,27 @@ import SubHeader from "component/subHeader";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
|
|
||||||
class RewardsPage extends React.PureComponent {
|
class RewardsPage extends React.PureComponent {
|
||||||
componentDidMount() {
|
/*
|
||||||
this.fetchRewards(this.props);
|
Below is broken for users who have claimed all rewards.
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
It can safely be disabled since we fetch all rewards after authentication, but should be re-enabled once fixed.
|
||||||
this.fetchRewards(nextProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchRewards(props) {
|
*/
|
||||||
const { fetching, rewards, fetchRewards } = props;
|
// componentDidMount() {
|
||||||
|
// this.fetchRewards(this.props);
|
||||||
if (!fetching && (!rewards || !rewards.length)) {
|
// }
|
||||||
fetchRewards();
|
//
|
||||||
}
|
// componentWillReceiveProps(nextProps) {
|
||||||
}
|
// this.fetchRewards(nextProps);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fetchRewards(props) {
|
||||||
|
// const { fetching, rewards, fetchRewards } = props;
|
||||||
|
//
|
||||||
|
// if (!fetching && (!rewards || !rewards.length)) {
|
||||||
|
// fetchRewards();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
renderPageHeader() {
|
renderPageHeader() {
|
||||||
const { doAuth, navigate, user } = this.props;
|
const { doAuth, navigate, user } = this.props;
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import * as settings from "constants/settings";
|
||||||
import { doClearCache } from "actions/app";
|
import { doClearCache } from "actions/app";
|
||||||
import {
|
import {
|
||||||
doSetDaemonSetting,
|
doSetDaemonSetting,
|
||||||
doSetClientSetting,
|
doSetClientSetting,
|
||||||
|
doGetThemes,
|
||||||
|
doSetTheme,
|
||||||
doChangeLanguage,
|
doChangeLanguage,
|
||||||
} from "actions/settings";
|
} from "actions/settings";
|
||||||
import {
|
import {
|
||||||
|
makeSelectClientSetting,
|
||||||
selectDaemonSettings,
|
selectDaemonSettings,
|
||||||
selectShowNsfw,
|
|
||||||
selectLanguages,
|
selectLanguages,
|
||||||
} from "selectors/settings";
|
} from "selectors/settings";
|
||||||
import { selectCurrentLanguage } from "selectors/app";
|
import { selectCurrentLanguage } from "selectors/app";
|
||||||
|
@ -16,7 +19,10 @@ import SettingsPage from "./view";
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
daemonSettings: selectDaemonSettings(state),
|
daemonSettings: selectDaemonSettings(state),
|
||||||
showNsfw: selectShowNsfw(state),
|
showNsfw: makeSelectClientSetting(settings.SHOW_NSFW)(state),
|
||||||
|
showUnavailable: makeSelectClientSetting(settings.SHOW_UNAVAILABLE)(state),
|
||||||
|
theme: makeSelectClientSetting(settings.THEME)(state),
|
||||||
|
themes: makeSelectClientSetting(settings.THEMES)(state),
|
||||||
language: selectCurrentLanguage(state),
|
language: selectCurrentLanguage(state),
|
||||||
languages: selectLanguages(state),
|
languages: selectLanguages(state),
|
||||||
});
|
});
|
||||||
|
@ -25,6 +31,7 @@ const perform = dispatch => ({
|
||||||
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
||||||
clearCache: () => dispatch(doClearCache()),
|
clearCache: () => dispatch(doClearCache()),
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
|
getThemes: () => dispatch(doGetThemes()),
|
||||||
changeLanguage: newLanguage => dispatch(doChangeLanguage(newLanguage)),
|
changeLanguage: newLanguage => dispatch(doChangeLanguage(newLanguage)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,13 @@ import * as settings from "constants/settings";
|
||||||
import lbry from "lbry.js";
|
import lbry from "lbry.js";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import FormFieldPrice from "component/formFieldPrice";
|
import FormFieldPrice from "component/formFieldPrice";
|
||||||
|
import { remote } from "electron";
|
||||||
const { remote } = require("electron");
|
|
||||||
|
|
||||||
class SettingsPage extends React.PureComponent {
|
class SettingsPage extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const { daemonSettings } = this.props || {};
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
// isMaxUpload: daemonSettings && daemonSettings.max_upload != 0,
|
|
||||||
// isMaxDownload: daemonSettings && daemonSettings.max_download != 0,
|
|
||||||
showUnavailable: lbry.getClientSetting(settings.SHOW_UNAVAILABLE),
|
|
||||||
language: lbry.getClientSetting(settings.LANGUAGE),
|
|
||||||
clearingCache: false,
|
clearingCache: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -41,11 +34,6 @@ class SettingsPage extends React.PureComponent {
|
||||||
this.props.setDaemonSetting(name, value);
|
this.props.setDaemonSetting(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
setClientSetting(name, value) {
|
|
||||||
lbry.setClientSetting(name, value);
|
|
||||||
this._onSettingSaveSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
onRunOnStartChange(event) {
|
onRunOnStartChange(event) {
|
||||||
this.setDaemonSetting("run_on_startup", event.target.checked);
|
this.setDaemonSetting("run_on_startup", event.target.checked);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +54,11 @@ class SettingsPage extends React.PureComponent {
|
||||||
this.setDaemonSetting("disable_max_key_fee", isDisabled);
|
this.setDaemonSetting("disable_max_key_fee", isDisabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onThemeChange(event) {
|
||||||
|
const { value } = event.target;
|
||||||
|
this.props.setClientSetting(settings.THEME, value);
|
||||||
|
}
|
||||||
|
|
||||||
// onMaxUploadPrefChange(isLimited) {
|
// onMaxUploadPrefChange(isLimited) {
|
||||||
// if (!isLimited) {
|
// if (!isLimited) {
|
||||||
// this.setDaemonSetting("max_upload", 0.0);
|
// this.setDaemonSetting("max_upload", 0.0);
|
||||||
|
@ -101,10 +94,29 @@ class SettingsPage extends React.PureComponent {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
onShowUnavailableChange(event) {}
|
onShowUnavailableChange(event) {
|
||||||
|
this.props.setClientSetting(
|
||||||
|
settings.SHOW_UNAVAILABLE,
|
||||||
|
event.target.checked
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.props.getThemes();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { daemonSettings, language, languages } = this.props;
|
const {
|
||||||
|
daemonSettings,
|
||||||
|
language,
|
||||||
|
languages,
|
||||||
|
showNsfw,
|
||||||
|
showUnavailable,
|
||||||
|
theme,
|
||||||
|
themes,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (!daemonSettings || Object.keys(daemonSettings).length === 0) {
|
if (!daemonSettings || Object.keys(daemonSettings).length === 0) {
|
||||||
return (
|
return (
|
||||||
|
@ -209,7 +221,7 @@ class SettingsPage extends React.PureComponent {
|
||||||
<FormRow
|
<FormRow
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
onChange={this.onShowUnavailableChange.bind(this)}
|
onChange={this.onShowUnavailableChange.bind(this)}
|
||||||
defaultChecked={this.state.showUnavailable}
|
defaultChecked={showUnavailable}
|
||||||
label={__("Show unavailable content in search results")}
|
label={__("Show unavailable content in search results")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -218,7 +230,7 @@ class SettingsPage extends React.PureComponent {
|
||||||
label={__("Show NSFW content")}
|
label={__("Show NSFW content")}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
onChange={this.onShowNsfwChange.bind(this)}
|
onChange={this.onShowNsfwChange.bind(this)}
|
||||||
defaultChecked={this.props.showNsfw}
|
defaultChecked={showNsfw}
|
||||||
helper={__(
|
helper={__(
|
||||||
"NSFW content may include nudity, intense sexuality, profanity, or other adult content. By displaying NSFW content, you are affirming you are of legal age to view mature content in your country or jurisdiction. "
|
"NSFW content may include nudity, intense sexuality, profanity, or other adult content. By displaying NSFW content, you are affirming you are of legal age to view mature content in your country or jurisdiction. "
|
||||||
)}
|
)}
|
||||||
|
@ -242,6 +254,27 @@ class SettingsPage extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="card">
|
||||||
|
<div className="card__content">
|
||||||
|
<h3>{__("Theme")}</h3>
|
||||||
|
</div>
|
||||||
|
<div className="card__content">
|
||||||
|
<FormField
|
||||||
|
type="select"
|
||||||
|
onChange={this.onThemeChange.bind(this)}
|
||||||
|
defaultValue={theme}
|
||||||
|
className="form-field__input--inline"
|
||||||
|
>
|
||||||
|
{themes.map((theme, index) =>
|
||||||
|
<option key={theme} value={theme}>
|
||||||
|
{theme}
|
||||||
|
</option>
|
||||||
|
)}
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<h3>{__("Application Cache")}</h3>
|
<h3>{__("Application Cache")}</h3>
|
||||||
|
|
|
@ -2,23 +2,16 @@ import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doResolveUri } from "actions/content";
|
import { doResolveUri } from "actions/content";
|
||||||
import { makeSelectClaimForUri } from "selectors/claims";
|
import { makeSelectClaimForUri } from "selectors/claims";
|
||||||
import { makeSelectIsResolvingForUri } from "selectors/content";
|
import { makeSelectIsUriResolving } from "selectors/content";
|
||||||
import ShowPage from "./view";
|
import ShowPage from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const select = (state, props) => ({
|
||||||
const selectClaim = makeSelectClaimForUri(),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
selectIsResolving = makeSelectIsResolvingForUri();
|
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||||
|
});
|
||||||
const select = (state, props) => ({
|
|
||||||
claim: selectClaim(state, props.params),
|
|
||||||
isResolvingUri: selectIsResolving(state, props.params),
|
|
||||||
});
|
|
||||||
|
|
||||||
return select;
|
|
||||||
};
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(ShowPage);
|
export default connect(select, perform)(ShowPage);
|
||||||
|
|
|
@ -6,15 +6,13 @@ import FilePage from "page/file";
|
||||||
|
|
||||||
class ShowPage extends React.PureComponent {
|
class ShowPage extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const { isResolvingUri, resolveUri, params } = this.props;
|
const { isResolvingUri, resolveUri, uri } = this.props;
|
||||||
const { uri } = params;
|
|
||||||
|
|
||||||
if (!isResolvingUri) resolveUri(uri);
|
if (!isResolvingUri) resolveUri(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
const { isResolvingUri, resolveUri, claim, params } = nextProps;
|
const { isResolvingUri, resolveUri, claim, uri } = nextProps;
|
||||||
const { uri } = params;
|
|
||||||
|
|
||||||
if (!isResolvingUri && claim === undefined && uri) {
|
if (!isResolvingUri && claim === undefined && uri) {
|
||||||
resolveUri(uri);
|
resolveUri(uri);
|
||||||
|
@ -22,8 +20,7 @@ class ShowPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { claim, params, isResolvingUri } = this.props;
|
const { claim, isResolvingUri, uri } = this.props;
|
||||||
const { uri } = params;
|
|
||||||
|
|
||||||
let innerContent = "";
|
let innerContent = "";
|
||||||
|
|
||||||
|
@ -39,6 +36,7 @@ class ShowPage extends React.PureComponent {
|
||||||
message={__("Loading magic decentralized data...")}
|
message={__("Loading magic decentralized data...")}
|
||||||
/>}
|
/>}
|
||||||
{claim === null &&
|
{claim === null &&
|
||||||
|
!isResolvingUri &&
|
||||||
<span className="empty">
|
<span className="empty">
|
||||||
{__("There's nothing at this location.")}
|
{__("There's nothing at this location.")}
|
||||||
</span>}
|
</span>}
|
||||||
|
|
|
@ -10,18 +10,26 @@ class TransactionHistoryPage extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { fetchingTransactions, transactions } = this.props;
|
const { fetchingTransactions, transactions } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<SubHeader />
|
<SubHeader />
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__title-primary">
|
<div
|
||||||
|
className={
|
||||||
|
"card__title-primary " +
|
||||||
|
(fetchingTransactions && transactions.length ? "reloading" : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
<h3>{__("Transaction History")}</h3>
|
<h3>{__("Transaction History")}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
{fetchingTransactions &&
|
{fetchingTransactions && !transactions.length
|
||||||
<BusyMessage message={__("Loading transactions")} />}
|
? <BusyMessage message={__("Loading transactions")} />
|
||||||
{!fetchingTransactions &&
|
: ""}
|
||||||
<TransactionList transactions={transactions} />}
|
{transactions && transactions.length
|
||||||
|
? <TransactionList transactions={transactions} />
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -8,6 +8,8 @@ const win = remote.BrowserWindow.getFocusedWindow();
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
isLoaded: false,
|
isLoaded: false,
|
||||||
|
modal: null,
|
||||||
|
modalProps: {},
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
|
upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
|
||||||
daemonVersionMatched: null,
|
daemonVersionMatched: null,
|
||||||
|
@ -76,14 +78,14 @@ reducers[types.UPDATE_VERSION] = function(state, action) {
|
||||||
reducers[types.OPEN_MODAL] = function(state, action) {
|
reducers[types.OPEN_MODAL] = function(state, action) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
modal: action.data.modal,
|
modal: action.data.modal,
|
||||||
modalExtraContent: action.data.extraContent,
|
modalProps: action.data.modalProps || {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[types.CLOSE_MODAL] = function(state, action) {
|
reducers[types.CLOSE_MODAL] = function(state, action) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
modal: undefined,
|
modal: undefined,
|
||||||
modalExtraContent: undefined,
|
modalProps: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
|
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
|
|
||||||
const defaultState = {};
|
const defaultState = {};
|
||||||
|
|
||||||
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
|
||||||
|
@ -49,7 +50,9 @@ reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
|
||||||
.filter(claimId => Object.keys(abandoningById).indexOf(claimId) === -1)
|
.filter(claimId => Object.keys(abandoningById).indexOf(claimId) === -1)
|
||||||
);
|
);
|
||||||
|
|
||||||
claims.filter(claim => claim.category.match(/claim/)).forEach(claim => {
|
claims
|
||||||
|
.filter(claim => claim.category && claim.category.match(/claim/))
|
||||||
|
.forEach(claim => {
|
||||||
byId[claim.claim_id] = claim;
|
byId[claim.claim_id] = claim;
|
||||||
|
|
||||||
const pending = Object.values(pendingById).find(pendingClaim => {
|
const pending = Object.values(pendingById).find(pendingClaim => {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as types from "constants/action_types";
|
||||||
|
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
|
playingUri: null,
|
||||||
rewardedContentClaimIds: [],
|
rewardedContentClaimIds: [],
|
||||||
channelPages: {},
|
channelPages: {},
|
||||||
};
|
};
|
||||||
|
@ -58,6 +59,12 @@ reducers[types.RESOLVE_URI_CANCELED] = reducers[
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reducers[types.SET_PLAYING_URI] = (state, action) => {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
playingUri: action.data.uri,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
|
// reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
|
||||||
// const channelPages = Object.assign({}, state.channelPages);
|
// const channelPages = Object.assign({}, state.channelPages);
|
||||||
// const { uri, claims } = action.data;
|
// const { uri, claims } = action.data;
|
||||||
|
@ -73,7 +80,7 @@ reducers[types.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED] = function(state, action) {
|
||||||
const channelPages = Object.assign({}, state.channelPages);
|
const channelPages = Object.assign({}, state.channelPages);
|
||||||
const { uri, totalClaims } = action.data;
|
const { uri, totalClaims } = action.data;
|
||||||
|
|
||||||
channelPages[uri] = totalClaims / 10;
|
channelPages[uri] = Math.ceil(totalClaims / 10);
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
channelPages,
|
channelPages,
|
||||||
|
|
|
@ -24,12 +24,6 @@ reducers[types.DAEMON_READY] = function(state, action) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[types.CHANGE_PATH] = function(state, action) {
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
currentPath: action.data.path,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
|
reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
pathAfterAuth: action.data.path,
|
pathAfterAuth: action.data.path,
|
||||||
|
@ -38,15 +32,16 @@ reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
|
||||||
|
|
||||||
reducers[types.HISTORY_NAVIGATE] = (state, action) => {
|
reducers[types.HISTORY_NAVIGATE] = (state, action) => {
|
||||||
const { stack, index } = state;
|
const { stack, index } = state;
|
||||||
|
|
||||||
let newState = {};
|
|
||||||
|
|
||||||
const path = action.data.url;
|
const path = action.data.url;
|
||||||
|
|
||||||
// Check for duplicated
|
let newState = {
|
||||||
|
currentPath: path,
|
||||||
|
};
|
||||||
|
|
||||||
if (action.data.index >= 0) {
|
if (action.data.index >= 0) {
|
||||||
newState.index = action.data.index;
|
newState.index = action.data.index;
|
||||||
} else if (!stack[index] || stack[index].path !== path) {
|
} else if (!stack[index] || stack[index].path !== path) {
|
||||||
|
// ^ Check for duplicated
|
||||||
newState.stack = [...stack.slice(0, index + 1), { path, scrollY: 0 }];
|
newState.stack = [...stack.slice(0, index + 1), { path, scrollY: 0 }];
|
||||||
newState.index = newState.stack.length - 1;
|
newState.index = newState.stack.length - 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
import lbryuri from "lbryuri";
|
|
||||||
|
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
const defaultState = {};
|
const defaultState = {};
|
||||||
|
@ -9,7 +8,6 @@ reducers[types.SEARCH_STARTED] = function(state, action) {
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
searching: true,
|
searching: true,
|
||||||
query: query,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,7 +29,6 @@ reducers[types.SEARCH_COMPLETED] = function(state, action) {
|
||||||
reducers[types.SEARCH_CANCELLED] = function(state, action) {
|
reducers[types.SEARCH_CANCELLED] = function(state, action) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
searching: false,
|
searching: false,
|
||||||
query: undefined,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,15 @@ import lbry from "lbry";
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
clientSettings: {
|
clientSettings: {
|
||||||
showNsfw: lbry.getClientSetting("showNsfw"),
|
showNsfw: lbry.getClientSetting(settings.SHOW_NSFW),
|
||||||
|
showUnavailable: lbry.getClientSetting(settings.SHOW_UNAVAILABLE),
|
||||||
welcome_acknowledged: lbry.getClientSetting(settings.NEW_USER_ACKNOWLEDGED),
|
welcome_acknowledged: lbry.getClientSetting(settings.NEW_USER_ACKNOWLEDGED),
|
||||||
credit_intro_acknowledged: lbry.getClientSetting(
|
credit_intro_acknowledged: lbry.getClientSetting(
|
||||||
settings.CREDIT_INTRO_ACKNOWLEDGED
|
settings.CREDIT_INTRO_ACKNOWLEDGED
|
||||||
),
|
),
|
||||||
language: lbry.getClientSetting(settings.LANGUAGE),
|
language: lbry.getClientSetting(settings.LANGUAGE),
|
||||||
|
theme: lbry.getClientSetting(settings.THEME),
|
||||||
|
themes: lbry.getClientSetting(settings.THEMES),
|
||||||
},
|
},
|
||||||
languages: {},
|
languages: {},
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,11 +10,12 @@ const buildDraftTransaction = () => ({
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
balance: undefined,
|
balance: undefined,
|
||||||
blocks: {},
|
blocks: {},
|
||||||
transactions: [],
|
transactions: {},
|
||||||
fetchingTransactions: false,
|
fetchingTransactions: false,
|
||||||
receiveAddress: address,
|
receiveAddress: address,
|
||||||
gettingNewAddress: false,
|
gettingNewAddress: false,
|
||||||
draftTransaction: buildDraftTransaction(),
|
draftTransaction: buildDraftTransaction(),
|
||||||
|
sendingSupport: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[types.FETCH_TRANSACTIONS_STARTED] = function(state, action) {
|
reducers[types.FETCH_TRANSACTIONS_STARTED] = function(state, action) {
|
||||||
|
@ -24,20 +25,16 @@ reducers[types.FETCH_TRANSACTIONS_STARTED] = function(state, action) {
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[types.FETCH_TRANSACTIONS_COMPLETED] = function(state, action) {
|
reducers[types.FETCH_TRANSACTIONS_COMPLETED] = function(state, action) {
|
||||||
const oldTransactions = Object.assign({}, state.transactions);
|
let byId = Object.assign({}, state.transactions);
|
||||||
const byId = Object.assign({}, oldTransactions.byId);
|
|
||||||
const { transactions } = action.data;
|
const { transactions } = action.data;
|
||||||
|
|
||||||
transactions.forEach(transaction => {
|
transactions.forEach(transaction => {
|
||||||
byId[transaction.txid] = transaction;
|
byId[transaction.txid] = transaction;
|
||||||
});
|
});
|
||||||
|
|
||||||
const newTransactions = Object.assign({}, oldTransactions, {
|
|
||||||
byId: byId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
transactions: newTransactions,
|
transactions: byId,
|
||||||
fetchingTransactions: false,
|
fetchingTransactions: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -125,6 +122,25 @@ reducers[types.SEND_TRANSACTION_FAILED] = function(state, action) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reducers[types.SUPPORT_TRANSACTION_STARTED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
sendingSupport: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[types.SUPPORT_TRANSACTION_COMPLETED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
sendingSupport: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[types.SUPPORT_TRANSACTION_FAILED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
error: action.data.error,
|
||||||
|
sendingSupport: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
reducers[types.FETCH_BLOCK_SUCCESS] = (state, action) => {
|
reducers[types.FETCH_BLOCK_SUCCESS] = (state, action) => {
|
||||||
const { block, block: { height } } = action.data,
|
const { block, block: { height } } = action.data,
|
||||||
blocks = Object.assign({}, state.blocks);
|
blocks = Object.assign({}, state.blocks);
|
||||||
|
|
|
@ -71,9 +71,9 @@ export const selectUpgradeDownloadItem = createSelector(
|
||||||
state => state.downloadItem
|
state => state.downloadItem
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectModalExtraContent = createSelector(
|
export const selectModalProps = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
state => state.modalExtraContent
|
state => state.modalProps
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectDaemonReady = createSelector(
|
export const selectDaemonReady = createSelector(
|
||||||
|
|
|
@ -7,14 +7,10 @@ export const selectAvailabilityByUri = createSelector(
|
||||||
state => state.byUri || {}
|
state => state.byUri || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectAvailabilityForUri = (state, props) => {
|
export const makeSelectIsAvailableForUri = uri => {
|
||||||
return selectAvailabilityByUri(state)[props.uri];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeSelectIsAvailableForUri = () => {
|
|
||||||
return createSelector(
|
return createSelector(
|
||||||
selectAvailabilityForUri,
|
selectAvailabilityByUri,
|
||||||
availability => (availability === undefined ? undefined : availability > 0)
|
byUri => (!byUri || byUri[uri] === undefined ? undefined : byUri[uri] > 0)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,10 +19,9 @@ export const selectFetchingAvailability = createSelector(
|
||||||
state => state.fetching || {}
|
state => state.fetching || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectFetchingAvailabilityForUri = (state, props) => {
|
export const makeSelectFetchingAvailabilityForUri = uri => {
|
||||||
return selectFetchingAvailability(state)[props.uri];
|
return createSelector(
|
||||||
};
|
selectFetchingAvailability,
|
||||||
|
byUri => byUri && byUri[uri]
|
||||||
export const makeSelectFetchingAvailabilityForUri = () => {
|
);
|
||||||
return createSelector(selectFetchingAvailabilityForUri, fetching => fetching);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
import { selectCurrentParams } from "selectors/navigation";
|
import { selectCurrentParams } from "selectors/navigation";
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
|
import { makeSelectCurrentParam } from "./navigation";
|
||||||
|
|
||||||
const _selectState = state => state.claims || {};
|
const _selectState = state => state.claims || {};
|
||||||
|
|
||||||
|
@ -40,25 +41,24 @@ export const selectAllClaimsByChannel = createSelector(
|
||||||
state => state.claimsByChannel || {}
|
state => state.claimsByChannel || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectClaimForUri = (state, props) => {
|
export const makeSelectClaimForUri = uri => {
|
||||||
const uri = lbryuri.normalize(props.uri);
|
return createSelector(
|
||||||
return selectClaimsByUri(state)[uri];
|
selectClaimsByUri,
|
||||||
|
claims => claims && claims[lbryuri.normalize(uri)]
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeSelectClaimForUri = () => {
|
export const makeSelectClaimIsMine = rawUri => {
|
||||||
return createSelector(selectClaimForUri, claim => claim);
|
const uri = lbryuri.normalize(rawUri);
|
||||||
};
|
return createSelector(
|
||||||
|
selectClaimsByUri,
|
||||||
const selectClaimForUriIsMine = (state, props) => {
|
selectMyClaimsRaw,
|
||||||
const uri = lbryuri.normalize(props.uri);
|
(claims, myClaims) =>
|
||||||
const claim = selectClaimsByUri(state)[uri];
|
claims &&
|
||||||
const myClaims = selectMyClaimsRaw(state);
|
claims[uri] &&
|
||||||
|
claims[uri].claim_id &&
|
||||||
return myClaims.has(claim.claim_id);
|
myClaims.has(claims[uri].claim_id)
|
||||||
};
|
);
|
||||||
|
|
||||||
export const makeSelectClaimForUriIsMine = () => {
|
|
||||||
return createSelector(selectClaimForUriIsMine, isMine => isMine);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const selectAllFetchingChannelClaims = createSelector(
|
export const selectAllFetchingChannelClaims = createSelector(
|
||||||
|
@ -66,84 +66,54 @@ export const selectAllFetchingChannelClaims = createSelector(
|
||||||
state => state.fetchingChannelClaims || {}
|
state => state.fetchingChannelClaims || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectFetchingChannelClaims = (state, props) => {
|
export const makeSelectFetchingChannelClaims = uri => {
|
||||||
const allFetchingChannelClaims = selectAllFetchingChannelClaims(state);
|
return createSelector(
|
||||||
|
selectAllFetchingChannelClaims,
|
||||||
return allFetchingChannelClaims[props.uri];
|
fetching => fetching && fetching[uri]
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeSelectFetchingChannelClaims = (state, props) => {
|
export const makeSelectClaimsInChannelForCurrentPage = uri => {
|
||||||
return createSelector(selectFetchingChannelClaims, fetching => fetching);
|
const pageSelector = makeSelectCurrentParam("page");
|
||||||
};
|
|
||||||
|
|
||||||
export const selectClaimsInChannelForUri = (state, props) => {
|
return createSelector(
|
||||||
const byId = selectClaimsById(state);
|
selectClaimsById,
|
||||||
const byChannel = selectAllClaimsByChannel(state)[props.uri] || {};
|
selectAllClaimsByChannel,
|
||||||
const claimIds = byChannel["all"];
|
pageSelector,
|
||||||
|
(byId, allClaims, page) => {
|
||||||
|
const byChannel = allClaims[uri] || {};
|
||||||
|
const claimIds = byChannel[page || 1];
|
||||||
|
|
||||||
if (!claimIds) return claimIds;
|
if (!claimIds) return claimIds;
|
||||||
|
|
||||||
const claims = [];
|
return claimIds.map(claimId => byId[claimId]);
|
||||||
|
}
|
||||||
claimIds.forEach(claimId => claims.push(byId[claimId]));
|
);
|
||||||
|
|
||||||
return claims;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeSelectClaimsInChannelForUri = () => {
|
export const makeSelectMetadataForUri = uri => {
|
||||||
return createSelector(selectClaimsInChannelForUri, claims => claims);
|
return createSelector(makeSelectClaimForUri(uri), claim => {
|
||||||
};
|
|
||||||
|
|
||||||
export const selectClaimsInChannelForCurrentPage = (state, props) => {
|
|
||||||
const byId = selectClaimsById(state);
|
|
||||||
const byChannel = selectAllClaimsByChannel(state)[props.uri] || {};
|
|
||||||
const params = selectCurrentParams(state);
|
|
||||||
const page = params && params.page ? params.page : 1;
|
|
||||||
const claimIds = byChannel[page];
|
|
||||||
|
|
||||||
if (!claimIds) return claimIds;
|
|
||||||
|
|
||||||
const claims = [];
|
|
||||||
|
|
||||||
claimIds.forEach(claimId => claims.push(byId[claimId]));
|
|
||||||
|
|
||||||
return claims;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeSelectClaimsInChannelForCurrentPage = () => {
|
|
||||||
return createSelector(selectClaimsInChannelForCurrentPage, claims => claims);
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectMetadataForUri = (state, props) => {
|
|
||||||
const claim = selectClaimForUri(state, props);
|
|
||||||
const metadata =
|
const metadata =
|
||||||
claim && claim.value && claim.value.stream && claim.value.stream.metadata;
|
claim && claim.value && claim.value.stream && claim.value.stream.metadata;
|
||||||
|
|
||||||
const value = metadata ? metadata : claim === undefined ? undefined : null;
|
const value = metadata ? metadata : claim === undefined ? undefined : null;
|
||||||
return value;
|
return value;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeSelectMetadataForUri = () => {
|
export const makeSelectTitleForUri = uri => {
|
||||||
return createSelector(selectMetadataForUri, metadata => metadata);
|
return createSelector(
|
||||||
|
makeSelectMetadataForUri(uri),
|
||||||
|
metadata => metadata && metadata.title
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectSourceForUri = (state, props) => {
|
export const makeSelectContentTypeForUri = uri => {
|
||||||
const claim = selectClaimForUri(state, props);
|
return createSelector(makeSelectClaimForUri(uri), claim => {
|
||||||
const source =
|
const source =
|
||||||
claim && claim.value && claim.value.stream && claim.value.stream.source;
|
claim && claim.value && claim.value.stream && claim.value.stream.source;
|
||||||
|
return source ? source.contentType : undefined;
|
||||||
return source ? source : claim === undefined ? undefined : null;
|
});
|
||||||
};
|
|
||||||
|
|
||||||
export const makeSelectSourceForUri = () => {
|
|
||||||
return createSelector(selectSourceForUri, source => source);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeSelectContentTypeForUri = () => {
|
|
||||||
return createSelector(
|
|
||||||
selectSourceForUri,
|
|
||||||
source => (source ? source.contentType : source)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const selectIsFetchingClaimListMine = createSelector(
|
export const selectIsFetchingClaimListMine = createSelector(
|
||||||
|
@ -210,7 +180,12 @@ export const selectMyChannelClaims = createSelector(
|
||||||
const ids = state.myChannelClaims || [];
|
const ids = state.myChannelClaims || [];
|
||||||
const claims = [];
|
const claims = [];
|
||||||
|
|
||||||
ids.forEach(id => claims.push(byId[id]));
|
ids.forEach(id => {
|
||||||
|
if (byId[id]) {
|
||||||
|
//I'm not sure why this check is necessary, but it ought to be a quick fix for https://github.com/lbryio/lbry-app/issues/544
|
||||||
|
claims.push(byId[id]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return claims;
|
return claims;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,16 @@ export const selectResolvingUris = createSelector(
|
||||||
state => state.resolvingUris || []
|
state => state.resolvingUris || []
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectResolvingUri = (state, props) => {
|
export const selectPlayingUri = createSelector(
|
||||||
return selectResolvingUris(state).indexOf(props.uri) != -1;
|
_selectState,
|
||||||
};
|
state => state.playingUri
|
||||||
|
);
|
||||||
|
|
||||||
export const makeSelectIsResolvingForUri = () => {
|
export const makeSelectIsUriResolving = uri => {
|
||||||
return createSelector(selectResolvingUri, resolving => resolving);
|
return createSelector(
|
||||||
|
selectResolvingUris,
|
||||||
|
resolvingUris => resolvingUris && resolvingUris.indexOf(uri) != -1
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const selectChannelPages = createSelector(
|
export const selectChannelPages = createSelector(
|
||||||
|
@ -30,12 +34,8 @@ export const selectChannelPages = createSelector(
|
||||||
state => state.channelPages || {}
|
state => state.channelPages || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectTotalPagesForChannel = (state, props) => {
|
export const makeSelectTotalPagesForChannel = uri => {
|
||||||
return selectChannelPages(state)[props.uri];
|
return createSelector(selectChannelPages, byUri => byUri && byUri[uri]);
|
||||||
};
|
|
||||||
|
|
||||||
export const makeSelectTotalPagesForChannel = () => {
|
|
||||||
return createSelector(selectTotalPagesForChannel, totalPages => totalPages);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const selectRewardContentClaimIds = createSelector(
|
export const selectRewardContentClaimIds = createSelector(
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue