mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-09-11 04:59:44 +00:00
Merge branch 'redesign' into v16-scrollbar
This commit is contained in:
commit
440d982690
83 changed files with 1273 additions and 1084 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+))?
|
||||||
|
|
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -8,12 +8,8 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
* Added a tipping button to send LBRY Credits to the publisher
|
*
|
||||||
* Added edit button on published content / improved UX for editing claims.
|
*
|
||||||
* 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.
|
|
||||||
* Improved scrollbar ( UI / UX ), now is less annoying and can be customized for different themes.
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
*
|
*
|
||||||
|
@ -31,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.16.0rc8",
|
"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,8 +9,10 @@ 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 -e "\033[1;31mPlatform detection failed\x1b[m"
|
echo -e "\033[1;31mPlatform detection failed\x1b[m"
|
||||||
|
@ -28,9 +29,13 @@ 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
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
7
ui/dist/themes/dark.css
vendored
7
ui/dist/themes/dark.css
vendored
|
@ -27,8 +27,8 @@
|
||||||
--search-border: 1px solid rgba(0,0,0, 0.25);
|
--search-border: 1px solid rgba(0,0,0, 0.25);
|
||||||
|
|
||||||
/* Tab */
|
/* Tab */
|
||||||
--tab-color: #757575;
|
--tab-color: rgba(255,255,255, 0.5) ;
|
||||||
--tab-active-color: #CCC;
|
--tab-active-color: rgba(255,255,255, 0.75);
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
--header-color: #CCC;
|
--header-color: #CCC;
|
||||||
|
@ -47,4 +47,7 @@
|
||||||
/* Scrollbar */
|
/* Scrollbar */
|
||||||
--scrollbar-thumb-bg: rgba(255, 255, 255, 0.20);
|
--scrollbar-thumb-bg: rgba(255, 255, 255, 0.20);
|
||||||
--scrollbar-thumb-hover-bg: rgba(255, 255, 255, 0.35);
|
--scrollbar-thumb-hover-bg: rgba(255, 255, 255, 0.35);
|
||||||
|
|
||||||
|
/* Divider */
|
||||||
|
--divider: 1px solid rgba(255,255,255, 0.12);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
import lbry from "lbry";
|
|
||||||
import { selectBalance } from "selectors/wallet";
|
|
||||||
import { doOpenModal, doShowSnackBar } from "actions/app";
|
|
||||||
import * as types from "constants/action_types";
|
|
||||||
import * as modals from "constants/modal_types";
|
|
||||||
|
|
||||||
export function doSendSupport(amount, claim_id) {
|
|
||||||
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"),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
dispatch({
|
|
||||||
type: types.SUPPORT_TRANSACTION_FAILED,
|
|
||||||
data: { error: results },
|
|
||||||
});
|
|
||||||
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const errorCallback = error => {
|
|
||||||
dispatch({
|
|
||||||
type: types.SUPPORT_TRANSACTION_FAILED,
|
|
||||||
data: { error: error.message },
|
|
||||||
});
|
|
||||||
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
|
|
||||||
};
|
|
||||||
|
|
||||||
lbry
|
|
||||||
.wallet_send({
|
|
||||||
claim_id: claim_id,
|
|
||||||
amount: amount,
|
|
||||||
})
|
|
||||||
.then(successCallback, errorCallback);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -322,38 +322,29 @@ export function doPurchaseUri(uri) {
|
||||||
const downloadingByOutpoint = selectDownloadingByOutpoint(state);
|
const downloadingByOutpoint = selectDownloadingByOutpoint(state);
|
||||||
const alreadyDownloading =
|
const alreadyDownloading =
|
||||||
fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
|
fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
|
||||||
|
|
||||||
// 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 = makeSelectCostInfoForUri(uri)(state);
|
const costInfo = makeSelectCostInfoForUri(uri)(state);
|
||||||
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(modals.AFFIRM_PURCHASE, { uri }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
return dispatch(doOpenModal(modals.AFFIRM_PURCHASE, { uri }));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,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;
|
||||||
|
@ -420,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);
|
||||||
|
// }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
selectBalance,
|
selectBalance,
|
||||||
} from "selectors/wallet";
|
} from "selectors/wallet";
|
||||||
import { doOpenModal, doShowSnackBar } from "actions/app";
|
import { doOpenModal, doShowSnackBar } from "actions/app";
|
||||||
|
import { doNavigate } from "actions/navigation";
|
||||||
import * as modals from "constants/modal_types";
|
import * as modals from "constants/modal_types";
|
||||||
|
|
||||||
export function doUpdateBalance(balance) {
|
export function doUpdateBalance(balance) {
|
||||||
|
@ -143,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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -70,8 +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"]),
|
||||||
fee: React.PropTypes.bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -79,10 +78,8 @@ 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,
|
||||||
fee: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -119,10 +116,7 @@ export class CreditAmount extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={`credit-amount credit-amount--${this.props.look} ${this.props
|
className={`credit-amount credit-amount--${this.props.look}`}
|
||||||
.fee
|
|
||||||
? " meta"
|
|
||||||
: ""}`}
|
|
||||||
title={fullPrice}
|
title={fullPrice}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -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,14 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { selectPlatform } from "selectors/app";
|
|
||||||
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
||||||
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
import { makeSelectCostInfoForUri } from "selectors/cost_info";
|
||||||
import { doOpenModal } from "actions/app";
|
import { doOpenModal } from "actions/app";
|
||||||
import { doFetchAvailability } from "actions/availability";
|
|
||||||
import { doOpenFileInShell, doOpenFileInFolder } from "actions/file_info";
|
|
||||||
import { makeSelectClaimIsMine } from "selectors/claims";
|
import { makeSelectClaimIsMine } from "selectors/claims";
|
||||||
import { doPurchaseUri, doLoadVideo, doStartDownload } from "actions/content";
|
|
||||||
import { doNavigate } from "actions/navigation";
|
|
||||||
import FileActions from "./view";
|
import FileActions from "./view";
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
@ -19,12 +14,7 @@ const select = (state, props) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
checkAvailability: uri => dispatch(doFetchAvailability(uri)),
|
|
||||||
openInShell: fileInfo => dispatch(doOpenFileInShell(fileInfo)),
|
|
||||||
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||||
startDownload: uri => dispatch(doPurchaseUri(uri)),
|
|
||||||
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
|
|
||||||
editClaim: fileInfo => dispatch(doNavigate("/publish", fileInfo)),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FileActions);
|
export default connect(select, perform)(FileActions);
|
||||||
|
|
|
@ -4,19 +4,11 @@ import FileDownloadLink from "component/fileDownloadLink";
|
||||||
import * as modals from "constants/modal_types";
|
import * as modals from "constants/modal_types";
|
||||||
|
|
||||||
class FileActions extends React.PureComponent {
|
class FileActions extends React.PureComponent {
|
||||||
handleSupportButtonClicked() {
|
|
||||||
this.props.onTipShow();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { fileInfo, uri, openModal, claimIsMine, editClaim } = this.props;
|
const { fileInfo, uri, openModal, claimIsMine } = this.props;
|
||||||
|
|
||||||
const name = fileInfo ? fileInfo.name : null;
|
const claimId = fileInfo ? fileInfo.claim_id : null,
|
||||||
const channel = fileInfo ? fileInfo.channel_name : null;
|
showDelete = fileInfo && Object.keys(fileInfo).length > 0;
|
||||||
|
|
||||||
const metadata = fileInfo ? fileInfo.metadata : null,
|
|
||||||
showMenu = fileInfo && Object.keys(fileInfo).length > 0,
|
|
||||||
title = metadata ? metadata.title : uri;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="card__actions">
|
<section className="card__actions">
|
||||||
|
@ -25,22 +17,25 @@ class FileActions extends React.PureComponent {
|
||||||
button="text"
|
button="text"
|
||||||
icon="icon-edit"
|
icon="icon-edit"
|
||||||
label={__("Edit")}
|
label={__("Edit")}
|
||||||
onClick={() => editClaim({ name, channel })}
|
navigate="/publish"
|
||||||
|
navigateParams={{ id: claimId }}
|
||||||
/>}
|
/>}
|
||||||
<FileDownloadLink uri={uri} />
|
<FileDownloadLink uri={uri} />
|
||||||
<Link
|
<Link
|
||||||
button="text"
|
button="text"
|
||||||
icon="icon-gift"
|
icon="icon-gift"
|
||||||
label={__("Support")}
|
label={__("Support")}
|
||||||
onClick={this.handleSupportButtonClicked.bind(this)}
|
navigate="/show"
|
||||||
|
navigateParams={{ uri, tab: "tip" }}
|
||||||
/>
|
/>
|
||||||
|
{showDelete &&
|
||||||
<Link
|
<Link
|
||||||
button="text"
|
button="text"
|
||||||
icon="icon-trash"
|
icon="icon-trash"
|
||||||
label={__("Remove")}
|
label={__("Remove")}
|
||||||
className="card__action--right"
|
className="card__action--right"
|
||||||
onClick={() => openModal(modals.CONFIRM_FILE_REMOVE, { uri })}
|
onClick={() => openModal(modals.CONFIRM_FILE_REMOVE, { uri })}
|
||||||
/>
|
/>}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
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;
|
|
@ -10,7 +10,6 @@ import { doFetchAvailability } from "actions/availability";
|
||||||
import { doOpenFileInShell } from "actions/file_info";
|
import { doOpenFileInShell } from "actions/file_info";
|
||||||
import { doPurchaseUri, doStartDownload } from "actions/content";
|
import { doPurchaseUri, doStartDownload } from "actions/content";
|
||||||
import FileDownloadLink from "./view";
|
import FileDownloadLink from "./view";
|
||||||
import * as modals from "constants/modal_types";
|
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
|
@ -22,9 +21,8 @@ const select = (state, props) => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
checkAvailability: uri => dispatch(doFetchAvailability(uri)),
|
checkAvailability: uri => dispatch(doFetchAvailability(uri)),
|
||||||
openInShell: fileInfo => dispatch(doOpenFileInShell(fileInfo)),
|
openInShell: path => dispatch(doOpenFileInShell(path)),
|
||||||
startDownload: uri =>
|
purchaseUri: uri => dispatch(doPurchaseUri(uri)),
|
||||||
dispatch(doPurchaseUri(uri, modals.CONFIRM_FILE_PURCHASE)),
|
|
||||||
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
|
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ class FileDownloadLink extends React.PureComponent {
|
||||||
downloading,
|
downloading,
|
||||||
uri,
|
uri,
|
||||||
openInShell,
|
openInShell,
|
||||||
startDownload,
|
purchaseUri,
|
||||||
costInfo,
|
costInfo,
|
||||||
loading,
|
loading,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -81,7 +81,7 @@ class FileDownloadLink extends React.PureComponent {
|
||||||
label={__("Download")}
|
label={__("Download")}
|
||||||
icon="icon-download"
|
icon="icon-download"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
startDownload(uri);
|
purchaseUri(uri);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -92,7 +92,7 @@ class FileDownloadLink extends React.PureComponent {
|
||||||
label={__("Open")}
|
label={__("Open")}
|
||||||
button="text"
|
button="text"
|
||||||
icon="icon-external-link-square"
|
icon="icon-external-link-square"
|
||||||
onClick={() => openInShell(fileInfo)}
|
onClick={() => openInShell(fileInfo.download_path)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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([
|
||||||
|
@ -131,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);
|
||||||
|
|
|
@ -12,6 +12,7 @@ const Link = props => {
|
||||||
disabled,
|
disabled,
|
||||||
children,
|
children,
|
||||||
navigate,
|
navigate,
|
||||||
|
navigateParams,
|
||||||
doNavigate,
|
doNavigate,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
@ -23,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;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -58,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,
|
||||||
});
|
});
|
||||||
|
@ -189,10 +186,10 @@ class PublishForm extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditClaim() {
|
handleEditClaim() {
|
||||||
const isMine = this.myClaimExists();
|
const claimInfo = this.claim() || this.myClaimInfo();
|
||||||
|
|
||||||
if (isMine) {
|
if (claimInfo) {
|
||||||
this.handlePrefillClicked();
|
this.handlePrefillClaim(claimInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,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,
|
||||||
|
@ -283,17 +281,21 @@ class PublishForm extends React.PureComponent {
|
||||||
description,
|
description,
|
||||||
language,
|
language,
|
||||||
nsfw,
|
nsfw,
|
||||||
} = claimInfo.value.stream.metadata;
|
} = metadata;
|
||||||
|
|
||||||
let newState = {
|
let newState = {
|
||||||
mode: "edit",
|
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -423,16 +425,11 @@ class PublishForm extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
let { name, channel } = this.props.params;
|
|
||||||
|
|
||||||
channel = channel || this.state.channel;
|
|
||||||
|
|
||||||
this.props.fetchClaimListMine();
|
this.props.fetchClaimListMine();
|
||||||
this._updateChannelList();
|
this._updateChannelList();
|
||||||
|
|
||||||
if (name) {
|
const { id } = this.props.params;
|
||||||
this.setState({ name, rawName: name, channel });
|
this.setState({ id });
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -461,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={__("Edit 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>
|
||||||
);
|
);
|
||||||
|
@ -500,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>
|
||||||
);
|
);
|
||||||
|
@ -532,11 +530,7 @@ class PublishForm extends React.PureComponent {
|
||||||
|
|
||||||
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>
|
||||||
|
@ -870,12 +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={submitLabel}
|
!this.state.submitting ? __("Publish") : __("Publishing...")
|
||||||
onClick={event => {
|
}
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
disabled={
|
disabled={
|
||||||
this.state.submitting ||
|
this.state.submitting ||
|
||||||
(this.state.uri &&
|
(this.state.uri &&
|
||||||
|
@ -890,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"}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { doSendSupport } from "actions/claims";
|
|
||||||
import TipLink from "./view";
|
|
||||||
|
|
||||||
const select = state => ({});
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
|
||||||
sendSupport: (amount, claim_id) => dispatch(doSendSupport(amount, claim_id)),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(select, perform)(TipLink);
|
|
|
@ -1,71 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import Link from "component/link";
|
|
||||||
import { FormRow } from "component/form";
|
|
||||||
|
|
||||||
class TipLink extends React.PureComponent {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
tipAmount: 1.0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSendButtonClicked() {
|
|
||||||
let claim_id = this.props.claim_id;
|
|
||||||
let amount = this.state.tipAmount;
|
|
||||||
this.props.sendSupport(amount, claim_id);
|
|
||||||
this.props.onTipHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSupportCancelButtonClicked() {
|
|
||||||
this.props.onTipHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSupportPriceChange(event) {
|
|
||||||
this.setState({
|
|
||||||
tipAmount: Number(event.target.value),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="card__content">
|
|
||||||
<div className="card__title-primary">
|
|
||||||
<h4>{__("Support")}</h4>
|
|
||||||
</div>
|
|
||||||
<div className="card__content">
|
|
||||||
{__(
|
|
||||||
"Support the creator and the success of their content by sending a tip. "
|
|
||||||
)}
|
|
||||||
<Link label={__("Learn more")} href="https://lbry.io/faq/tipping" />
|
|
||||||
</div>
|
|
||||||
<div className="card__content">
|
|
||||||
<FormRow
|
|
||||||
label={__("Amount")}
|
|
||||||
postfix={__("LBC")}
|
|
||||||
min="0"
|
|
||||||
step="0.1"
|
|
||||||
type="number"
|
|
||||||
placeholder="1.00"
|
|
||||||
onChange={event => this.handleSupportPriceChange(event)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="card__actions">
|
|
||||||
<Link
|
|
||||||
label={__("Send")}
|
|
||||||
button="primary"
|
|
||||||
onClick={this.handleSendButtonClicked.bind(this)}
|
|
||||||
/>
|
|
||||||
<Link
|
|
||||||
label={__("Cancel")}
|
|
||||||
button="alt"
|
|
||||||
onClick={this.handleSupportCancelButtonClicked.bind(this)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TipLink;
|
|
|
@ -1,8 +1,13 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doNavigate } from "actions/navigation";
|
import { doNavigate } from "actions/navigation";
|
||||||
|
import { selectClaimedRewardsByTransactionId } from "selectors/rewards";
|
||||||
import TransactionList from "./view";
|
import TransactionList from "./view";
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
rewards: selectClaimedRewardsByTransactionId(state),
|
||||||
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,140 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import LinkTransaction from "component/linkTransaction";
|
|
||||||
import { CreditAmount } from "component/common";
|
|
||||||
|
|
||||||
class TransactionTableBody extends React.PureComponent {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
getClaimLink(claim_name, claim_id) {
|
|
||||||
let uri = `lbry://${claim_name}#${claim_id}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<a className="button-text" onClick={() => this.props.navigate(uri)}>
|
|
||||||
{claim_name}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
filterList(transaction) {
|
|
||||||
if (this.props.filter == "claim") {
|
|
||||||
return transaction.claim_info.length > 0;
|
|
||||||
} else if (this.props.filter == "support") {
|
|
||||||
return transaction.support_info.length > 0;
|
|
||||||
} else if (this.props.filter == "update") {
|
|
||||||
return transaction.update_info.length > 0;
|
|
||||||
} else {
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBody(transaction) {
|
|
||||||
const txid = transaction.id;
|
|
||||||
const date = transaction.date;
|
|
||||||
const fee = transaction.fee;
|
|
||||||
const filter = this.props.filter;
|
|
||||||
const options = {
|
|
||||||
weekday: "short",
|
|
||||||
year: "2-digit",
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (filter == "tipSupport")
|
|
||||||
transaction["tipSupport_info"] = transaction["support_info"].filter(
|
|
||||||
tx => tx.is_tip
|
|
||||||
);
|
|
||||||
|
|
||||||
return filter != "unfiltered"
|
|
||||||
? transaction[`${filter}_info`].map(item => {
|
|
||||||
return (
|
|
||||||
<tr key={`${txid}:${item.nout}`}>
|
|
||||||
<td>
|
|
||||||
{date
|
|
||||||
? date.toLocaleDateString("en-US", options)
|
|
||||||
: <span className="empty">
|
|
||||||
{__("(Transaction pending)")}
|
|
||||||
</span>}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<CreditAmount
|
|
||||||
amount={item.amount}
|
|
||||||
look="plain"
|
|
||||||
label={false}
|
|
||||||
showPlus={true}
|
|
||||||
precision={8}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<CreditAmount
|
|
||||||
amount={fee}
|
|
||||||
look="plain"
|
|
||||||
fee={true}
|
|
||||||
label={false}
|
|
||||||
precision={8}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{this.getClaimLink(item.claim_name, item.claim_id)}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<LinkTransaction id={txid} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
: <tr key={txid}>
|
|
||||||
<td>
|
|
||||||
{date
|
|
||||||
? date.toLocaleDateString("en-US", options)
|
|
||||||
: <span className="empty">
|
|
||||||
{__("(Transaction pending)")}
|
|
||||||
</span>}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<CreditAmount
|
|
||||||
amount={transaction.amount}
|
|
||||||
look="plain"
|
|
||||||
label={false}
|
|
||||||
showPlus={true}
|
|
||||||
precision={8}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<CreditAmount
|
|
||||||
amount={fee}
|
|
||||||
look="plain"
|
|
||||||
fee={true}
|
|
||||||
label={false}
|
|
||||||
precision={8}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<LinkTransaction id={txid} />
|
|
||||||
</td>
|
|
||||||
</tr>;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeFeeTx(transaction) {
|
|
||||||
if (this.props.filter == "unfiltered")
|
|
||||||
return Math.abs(transaction.amount) != Math.abs(transaction.fee);
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { transactions, filter } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tbody>
|
|
||||||
{transactions
|
|
||||||
.filter(this.filterList, this)
|
|
||||||
.filter(this.removeFeeTx, this)
|
|
||||||
.map(this.renderBody, this)}
|
|
||||||
</tbody>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TransactionTableBody;
|
|
|
@ -1,19 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
class TransactionTableHeader extends React.PureComponent {
|
|
||||||
render() {
|
|
||||||
const { filter } = this.props;
|
|
||||||
return (
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{__("Date")}</th>
|
|
||||||
<th>{__("Amount(Fee)")}</th>
|
|
||||||
{filter != "unfiltered" && <th> {__("Claim Name")} </th>}
|
|
||||||
<th>{__("Transaction")}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TransactionTableHeader;
|
|
|
@ -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,6 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import TransactionTableHeader from "./internal/TransactionListHeader";
|
import TransactionListItem from "./internal/TransactionListItem";
|
||||||
import TransactionTableBody from "./internal/TransactionListBody";
|
|
||||||
import FormField from "component/formField";
|
import FormField from "component/formField";
|
||||||
|
|
||||||
class TransactionList extends React.PureComponent {
|
class TransactionList extends React.PureComponent {
|
||||||
|
@ -8,7 +7,7 @@ class TransactionList extends React.PureComponent {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filter: "unfiltered",
|
filter: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,45 +17,63 @@ class TransactionList extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClaimNameClicked(uri) {
|
filterTransaction(transaction) {
|
||||||
this.props.navigate("/show", { uri });
|
const { filter } = this.state;
|
||||||
|
|
||||||
|
return !filter || filter == transaction.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { emptyMessage, transactions } = this.props;
|
const { emptyMessage, rewards, transactions } = this.props;
|
||||||
const { filter } = this.state;
|
|
||||||
|
|
||||||
if (!transactions || !transactions.length) {
|
let transactionList = transactions.filter(
|
||||||
return (
|
this.filterTransaction.bind(this)
|
||||||
<div className="empty">
|
|
||||||
{emptyMessage || __("No transactions to list.")}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
{(transactionList.length || this.state.filter) &&
|
||||||
<span className="sort-section">
|
<span className="sort-section">
|
||||||
{__("Filter")} {" "}
|
{__("Filter")} {" "}
|
||||||
<FormField
|
<FormField
|
||||||
type="select"
|
type="select"
|
||||||
onChange={this.handleFilterChanged.bind(this)}
|
onChange={this.handleFilterChanged.bind(this)}
|
||||||
>
|
>
|
||||||
<option value="unfiltered">{__("All")}</option>
|
<option value="">{__("All")}</option>
|
||||||
<option value="claim">{__("Publishes")}</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="support">{__("Supports")}</option>
|
||||||
<option value="tipSupport">{__("Tips")}</option>
|
|
||||||
<option value="update">{__("Updates")}</option>
|
<option value="update">{__("Updates")}</option>
|
||||||
</FormField>
|
</FormField>
|
||||||
</span>
|
</span>}
|
||||||
<table className="table-standard table-stretch">
|
{!transactionList.length &&
|
||||||
<TransactionTableHeader filter={filter} />
|
<div className="empty">
|
||||||
<TransactionTableBody
|
{emptyMessage || __("No transactions to list.")}
|
||||||
transactions={transactions}
|
</div>}
|
||||||
filter={filter}
|
{Boolean(transactionList.length) &&
|
||||||
navigate={this.handleClaimNameClicked.bind(this)}
|
<table className="table-standard table-transactions table-stretch">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{__("Date")}</th>
|
||||||
|
<th>{__("Amount (Fee)")}</th>
|
||||||
|
<th>{__("Type")} </th>
|
||||||
|
<th>{__("Details")} </th>
|
||||||
|
<th>{__("Transaction")}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{transactionList.map(t =>
|
||||||
|
<TransactionListItem
|
||||||
|
key={`${t.txid}:${t.nout}`}
|
||||||
|
transaction={t}
|
||||||
|
reward={rewards && rewards[t.txid]}
|
||||||
/>
|
/>
|
||||||
</table>
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,6 +15,7 @@ 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 select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||||
|
@ -24,13 +24,14 @@ const select = (state, props) => ({
|
||||||
obscureNsfw: !selectShowNsfw(state),
|
obscureNsfw: !selectShowNsfw(state),
|
||||||
isLoading: makeSelectLoadingForUri(props.uri)(state),
|
isLoading: makeSelectLoadingForUri(props.uri)(state),
|
||||||
isDownloading: makeSelectDownloadingForUri(props.uri)(state),
|
isDownloading: makeSelectDownloadingForUri(props.uri)(state),
|
||||||
|
playingUri: selectPlayingUri(state),
|
||||||
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
||||||
volume: selectVolume(state),
|
volume: selectVolume(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
loadVideo: uri => dispatch(doLoadVideo(uri)),
|
play: uri => dispatch(doPlayUri(uri)),
|
||||||
purchaseUri: uri => dispatch(doPurchaseUri(uri)),
|
cancelPlay: () => dispatch(doSetPlayingUri(null)),
|
||||||
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,18 +17,12 @@ class VideoPlayButton extends React.PureComponent {
|
||||||
"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();
|
|
||||||
} else {
|
|
||||||
alert("fix me set pending play");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -54,7 +48,7 @@ class VideoPlayButton extends React.PureComponent {
|
||||||
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()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,14 +1,31 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "component/link";
|
import { Form, FormRow, Submit } from "component/form";
|
||||||
import { FormRow } from "component/form";
|
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
|
|
||||||
const WalletSend = props => {
|
class WalletSend extends React.PureComponent {
|
||||||
const { sendToAddress, setAmount, setAddress, amount, address } = props;
|
handleSubmit() {
|
||||||
|
const { amount, address, sendToAddress } = this.props;
|
||||||
|
const validSubmit = parseFloat(amount) > 0.0 && address;
|
||||||
|
|
||||||
|
if (validSubmit) {
|
||||||
|
sendToAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
closeModal,
|
||||||
|
modal,
|
||||||
|
setAmount,
|
||||||
|
setAddress,
|
||||||
|
amount,
|
||||||
|
address,
|
||||||
|
error,
|
||||||
|
} = 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>
|
||||||
|
@ -37,18 +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>
|
||||||
</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";
|
||||||
|
@ -132,11 +137,6 @@ export const CLAIM_REWARD_FAILURE = "CLAIM_REWARD_FAILURE";
|
||||||
export const CLAIM_REWARD_CLEAR_ERROR = "CLAIM_REWARD_CLEAR_ERROR";
|
export const CLAIM_REWARD_CLEAR_ERROR = "CLAIM_REWARD_CLEAR_ERROR";
|
||||||
export const FETCH_REWARD_CONTENT_COMPLETED = "FETCH_REWARD_CONTENT_COMPLETED";
|
export const FETCH_REWARD_CONTENT_COMPLETED = "FETCH_REWARD_CONTENT_COMPLETED";
|
||||||
|
|
||||||
// Supports
|
|
||||||
export const SUPPORT_TRANSACTION_STARTED = "SUPPORT_TRANSACTION_STARTED";
|
|
||||||
export const SUPPORT_TRANSACTION_COMPLETED = "SUPPORT_TRANSACTION_COMPLETED";
|
|
||||||
export const SUPPORT_TRANSACTION_FAILED = "SUPPORT_TRANSACTION_FAILED";
|
|
||||||
|
|
||||||
//Language
|
//Language
|
||||||
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED";
|
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED";
|
||||||
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED";
|
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED";
|
||||||
|
|
|
@ -176,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.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
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 { doLoadVideo } from "actions/content";
|
import { doLoadVideo, doSetPlayingUri } from "actions/content";
|
||||||
import { makeSelectMetadataForUri } from "selectors/claims";
|
import { makeSelectMetadataForUri } from "selectors/claims";
|
||||||
import ModalAffirmPurchase from "./view";
|
import ModalAffirmPurchase from "./view";
|
||||||
|
|
||||||
|
@ -10,6 +10,10 @@ const select = (state, props) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
cancelPurchase: () => {
|
||||||
|
dispatch(doSetPlayingUri(null));
|
||||||
|
dispatch(doCloseModal());
|
||||||
|
},
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
loadVideo: uri => dispatch(doLoadVideo(uri)),
|
loadVideo: uri => dispatch(doLoadVideo(uri)),
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,7 @@ class ModalAffirmPurchase extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { closeModal, metadata: { title }, uri } = this.props;
|
const { cancelPurchase, metadata: { title }, uri } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -17,7 +17,7 @@ class ModalAffirmPurchase extends React.PureComponent {
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
contentLabel={__("Confirm Purchase")}
|
contentLabel={__("Confirm Purchase")}
|
||||||
onConfirmed={this.onAffirmPurchase.bind(this)}
|
onConfirmed={this.onAffirmPurchase.bind(this)}
|
||||||
onAborted={closeModal}
|
onAborted={cancelPurchase}
|
||||||
>
|
>
|
||||||
{__("This will purchase")} <strong>{title}</strong> {__("for")}{" "}
|
{__("This will purchase")} <strong>{title}</strong> {__("for")}{" "}
|
||||||
<strong>
|
<strong>
|
||||||
|
|
|
@ -2,16 +2,13 @@ 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 {
|
import { makeSelectTitleForUri, makeSelectClaimIsMine } from "selectors/claims";
|
||||||
makeSelectMetadataForUri,
|
|
||||||
makeSelectClaimIsMine,
|
|
||||||
} from "selectors/claims";
|
|
||||||
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
import { makeSelectFileInfoForUri } from "selectors/file_info";
|
||||||
import ModalRemoveFile from "./view";
|
import ModalRemoveFile from "./view";
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
metadata: makeSelectMetadataForUri(props.uri)(state),
|
title: makeSelectTitleForUri(props.uri)(state),
|
||||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ class ModalRemoveFile extends React.PureComponent {
|
||||||
closeModal,
|
closeModal,
|
||||||
deleteFile,
|
deleteFile,
|
||||||
fileInfo: { outpoint },
|
fileInfo: { outpoint },
|
||||||
metadata: { title },
|
title,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { deleteChecked, abandonClaimChecked } = this.state;
|
const { deleteChecked, abandonClaimChecked } = this.state;
|
||||||
|
|
||||||
|
|
|
@ -9,18 +9,19 @@ 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 select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
claimsInChannel: makeSelectClaimsInChannelForCurrentPage(
|
claimsInChannel: makeSelectClaimsInChannelForCurrentPage(props.uri)(state),
|
||||||
props.uri,
|
|
||||||
props.page
|
|
||||||
)(state),
|
|
||||||
fetching: makeSelectFetchingChannelClaims(props.uri)(state),
|
fetching: makeSelectFetchingChannelClaims(props.uri)(state),
|
||||||
|
page: makeSelectCurrentParam("page")(state),
|
||||||
params: selectCurrentParams(state),
|
params: selectCurrentParams(state),
|
||||||
totalPages: makeSelectTotalPagesForChannel(props.uri)(state),
|
totalPages: makeSelectTotalPagesForChannel(props.uri)(state),
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,6 @@ 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 {
|
||||||
|
@ -16,7 +15,7 @@ class ChannelPage extends React.PureComponent {
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
const { page, uri, fetching, fetchClaims, fetchClaimCount } = this.props;
|
const { page, uri, fetching, fetchClaims, fetchClaimCount } = this.props;
|
||||||
|
|
||||||
if (fetching !== nextProps.page && page !== nextProps.page) {
|
if (nextProps.page && page !== nextProps.page) {
|
||||||
fetchClaims(nextProps.uri, nextProps.page);
|
fetchClaims(nextProps.uri, nextProps.page);
|
||||||
}
|
}
|
||||||
if (nextProps.uri != uri) {
|
if (nextProps.uri != uri) {
|
||||||
|
@ -25,7 +24,7 @@ class ChannelPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@ -42,10 +41,10 @@ class ChannelPage extends React.PureComponent {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
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,6 +13,7 @@ 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 select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
@ -20,6 +21,7 @@ const select = (state, props) => ({
|
||||||
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||||
metadata: makeSelectMetadataForUri(props.uri)(state),
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
obscureNsfw: !selectShowNsfw(state),
|
obscureNsfw: !selectShowNsfw(state),
|
||||||
|
tab: makeSelectCurrentParam("tab")(state),
|
||||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,55 +1,15 @@
|
||||||
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 TipLink from "component/tipLink";
|
|
||||||
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 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>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
class FilePage extends React.PureComponent {
|
class FilePage extends React.PureComponent {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
showTipBox: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.fetchFileInfo(this.props);
|
this.fetchFileInfo(this.props);
|
||||||
this.fetchCostInfo(this.props);
|
this.fetchCostInfo(this.props);
|
||||||
|
@ -71,29 +31,18 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTipShow() {
|
|
||||||
this.setState({
|
|
||||||
showTipBox: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTipHide() {
|
|
||||||
this.setState({
|
|
||||||
showTipBox: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
claim,
|
claim,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
metadata,
|
metadata,
|
||||||
contentType,
|
contentType,
|
||||||
|
tab,
|
||||||
uri,
|
uri,
|
||||||
rewardedContentClaimIds,
|
rewardedContentClaimIds,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { showTipBox } = this.state;
|
const showTipBox = tab == "tip";
|
||||||
|
|
||||||
if (!claim || !metadata) {
|
if (!claim || !metadata) {
|
||||||
return (
|
return (
|
||||||
|
@ -101,24 +50,7 @@ class FilePage extends React.PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
||||||
|
@ -128,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} />
|
||||||
|
@ -138,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" }}>
|
||||||
|
@ -147,55 +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>
|
||||||
<FileActions
|
|
||||||
uri={uri}
|
|
||||||
onTipShow={this.handleTipShow.bind(this)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{!showTipBox &&
|
<FileDetails uri={uri} />
|
||||||
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
|
||||||
<ReactMarkdown
|
|
||||||
source={(metadata && metadata.description) || ""}
|
|
||||||
escapeHtml={true}
|
|
||||||
disallowedTypes={["Heading", "HtmlInline", "HtmlBlock"]}
|
|
||||||
/>
|
|
||||||
</div>}
|
</div>}
|
||||||
|
{tab === "tip" &&
|
||||||
|
<WalletSendTip claim_id={claim.claim_id} uri={uri} />}
|
||||||
</div>
|
</div>
|
||||||
{metadata && claim && !showTipBox
|
|
||||||
? <div className="card__content">
|
|
||||||
<FormatItem
|
|
||||||
metadata={metadata}
|
|
||||||
contentType={contentType}
|
|
||||||
claim={claim}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
: ""}
|
|
||||||
{showTipBox
|
|
||||||
? <TipLink
|
|
||||||
onTipShow={this.handleTipShow.bind(this)}
|
|
||||||
onTipHide={this.handleTipHide.bind(this)}
|
|
||||||
claim_id={claim.claim_id}
|
|
||||||
/>
|
|
||||||
: ""}
|
|
||||||
{!showTipBox &&
|
|
||||||
<div className="card__content">
|
|
||||||
<Link
|
|
||||||
href={`https://lbry.io/dmca?claim_id=${claim.claim_id}`}
|
|
||||||
label={__("report")}
|
|
||||||
className="button-text-help"
|
|
||||||
/>
|
|
||||||
</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;
|
||||||
|
|
|
@ -36,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>
|
||||||
|
|
|
@ -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 => {
|
||||||
|
@ -189,31 +192,6 @@ reducers[types.CREATE_CHANNEL_COMPLETED] = function(state, action) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[types.SUPPORT_TRANSACTION_STARTED] = function(state, action) {
|
|
||||||
const newSupportTransaction = Object.assign({}, state.supportTransaction, {
|
|
||||||
sendingSupport: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
supportTransaction: newSupportTransaction,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
reducers[types.SUPPORT_TRANSACTION_COMPLETED] = function(state, action) {
|
|
||||||
return Object.assign({}, state);
|
|
||||||
};
|
|
||||||
|
|
||||||
reducers[types.SUPPORT_TRANSACTION_FAILED] = function(state, action) {
|
|
||||||
const newSupportTransaction = Object.assign({}, state.supportTransaction, {
|
|
||||||
sendingSupport: false,
|
|
||||||
error: action.data.error,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
supportTransaction: newSupportTransaction,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function reducer(state = defaultState, action) {
|
export default function reducer(state = defaultState, action) {
|
||||||
const handler = reducers[action.type];
|
const handler = reducers[action.type];
|
||||||
if (handler) return handler(state, action);
|
if (handler) return handler(state, action);
|
||||||
|
|
|
@ -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,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 || {};
|
||||||
|
|
||||||
|
@ -66,19 +67,22 @@ export const selectAllFetchingChannelClaims = createSelector(
|
||||||
);
|
);
|
||||||
|
|
||||||
export const makeSelectFetchingChannelClaims = uri => {
|
export const makeSelectFetchingChannelClaims = uri => {
|
||||||
createSelector(
|
return createSelector(
|
||||||
selectAllFetchingChannelClaims,
|
selectAllFetchingChannelClaims,
|
||||||
fetching => fetching && fetching[uri]
|
fetching => fetching && fetching[uri]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeSelectClaimsInChannelForCurrentPage = (uri, page = 1) => {
|
export const makeSelectClaimsInChannelForCurrentPage = uri => {
|
||||||
|
const pageSelector = makeSelectCurrentParam("page");
|
||||||
|
|
||||||
return createSelector(
|
return createSelector(
|
||||||
selectClaimsById,
|
selectClaimsById,
|
||||||
selectAllClaimsByChannel,
|
selectAllClaimsByChannel,
|
||||||
(byId, allClaims) => {
|
pageSelector,
|
||||||
|
(byId, allClaims, page) => {
|
||||||
const byChannel = allClaims[uri] || {};
|
const byChannel = allClaims[uri] || {};
|
||||||
const claimIds = byChannel[page];
|
const claimIds = byChannel[page || 1];
|
||||||
|
|
||||||
if (!claimIds) return claimIds;
|
if (!claimIds) return claimIds;
|
||||||
|
|
||||||
|
@ -97,6 +101,13 @@ export const makeSelectMetadataForUri = uri => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const makeSelectTitleForUri = uri => {
|
||||||
|
return createSelector(
|
||||||
|
makeSelectMetadataForUri(uri),
|
||||||
|
metadata => metadata && metadata.title
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const makeSelectContentTypeForUri = uri => {
|
export const makeSelectContentTypeForUri = uri => {
|
||||||
return createSelector(makeSelectClaimForUri(uri), claim => {
|
return createSelector(makeSelectClaimForUri(uri), claim => {
|
||||||
const source =
|
const source =
|
||||||
|
|
|
@ -17,6 +17,11 @@ export const selectResolvingUris = createSelector(
|
||||||
state => state.resolvingUris || []
|
state => state.resolvingUris || []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectPlayingUri = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => state.playingUri
|
||||||
|
);
|
||||||
|
|
||||||
export const makeSelectIsUriResolving = uri => {
|
export const makeSelectIsUriResolving = uri => {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
selectResolvingUris,
|
selectResolvingUris,
|
||||||
|
|
|
@ -10,8 +10,11 @@ export const selectCurrentPath = createSelector(
|
||||||
state => state.currentPath
|
state => state.currentPath
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const computePageFromPath = path =>
|
||||||
|
path.replace(/^\//, "").split("?")[0];
|
||||||
|
|
||||||
export const selectCurrentPage = createSelector(selectCurrentPath, path => {
|
export const selectCurrentPage = createSelector(selectCurrentPath, path => {
|
||||||
return path.replace(/^\//, "").split("?")[0];
|
return computePageFromPath(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const selectCurrentParams = createSelector(selectCurrentPath, path => {
|
export const selectCurrentParams = createSelector(selectCurrentPath, path => {
|
||||||
|
@ -87,7 +90,7 @@ export const selectPageTitle = createSelector(
|
||||||
case "start":
|
case "start":
|
||||||
return __("Start");
|
return __("Start");
|
||||||
case "publish":
|
case "publish":
|
||||||
return __("Publish");
|
return params.id ? __("Edit") : __("Publish");
|
||||||
case "help":
|
case "help":
|
||||||
return __("Help");
|
return __("Help");
|
||||||
case "developer":
|
case "developer":
|
||||||
|
|
|
@ -19,6 +19,15 @@ export const selectClaimedRewards = createSelector(
|
||||||
byId => Object.values(byId) || []
|
byId => Object.values(byId) || []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectClaimedRewardsByTransactionId = createSelector(
|
||||||
|
selectClaimedRewards,
|
||||||
|
rewards =>
|
||||||
|
rewards.reduce((map, reward) => {
|
||||||
|
map[reward.transaction_id] = reward;
|
||||||
|
return map;
|
||||||
|
}, {})
|
||||||
|
);
|
||||||
|
|
||||||
export const selectUnclaimedRewards = createSelector(
|
export const selectUnclaimedRewards = createSelector(
|
||||||
selectUnclaimedRewardsByType,
|
selectUnclaimedRewardsByType,
|
||||||
byType =>
|
byType =>
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
import { selectPageTitle, selectCurrentPage } from "selectors/navigation";
|
import {
|
||||||
|
selectPageTitle,
|
||||||
|
selectCurrentPage,
|
||||||
|
selectCurrentParams,
|
||||||
|
} from "selectors/navigation";
|
||||||
|
|
||||||
export const _selectState = state => state.search || {};
|
export const _selectState = state => state.search || {};
|
||||||
|
|
||||||
export const selectSearchQuery = createSelector(
|
export const selectSearchQuery = createSelector(
|
||||||
_selectState,
|
selectCurrentPage,
|
||||||
state => state.query
|
selectCurrentParams,
|
||||||
|
(page, params) => (page === "search" ? params && params.query : null)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectIsSearching = createSelector(
|
export const selectIsSearching = createSelector(
|
||||||
|
@ -36,7 +41,10 @@ export const selectWunderBarAddress = createSelector(
|
||||||
(page, title, query) => (page != "search" ? title : query ? query : title)
|
(page, title, query) => (page != "search" ? title : query ? query : title)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectWunderBarIcon = createSelector(selectCurrentPage, page => {
|
export const selectWunderBarIcon = createSelector(
|
||||||
|
selectCurrentPage,
|
||||||
|
selectCurrentParams,
|
||||||
|
(page, params) => {
|
||||||
switch (page) {
|
switch (page) {
|
||||||
case "auth":
|
case "auth":
|
||||||
return "icon-user";
|
return "icon-user";
|
||||||
|
@ -69,7 +77,7 @@ export const selectWunderBarIcon = createSelector(selectCurrentPage, page => {
|
||||||
case "show":
|
case "show":
|
||||||
return "icon-file";
|
return "icon-file";
|
||||||
case "publish":
|
case "publish":
|
||||||
return "icon-upload";
|
return params.id ? __("icon-pencil") : __("icon-upload");
|
||||||
case "developer":
|
case "developer":
|
||||||
return "icon-code";
|
return "icon-code";
|
||||||
case "discover":
|
case "discover":
|
||||||
|
@ -77,4 +85,5 @@ export const selectWunderBarIcon = createSelector(selectCurrentPage, page => {
|
||||||
default:
|
default:
|
||||||
return "icon-file";
|
return "icon-file";
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -7,34 +7,76 @@ export const selectBalance = createSelector(
|
||||||
state => state.balance
|
state => state.balance
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectTransactions = createSelector(
|
|
||||||
_selectState,
|
|
||||||
state => state.transactions || {}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectTransactionsById = createSelector(
|
export const selectTransactionsById = createSelector(
|
||||||
selectTransactions,
|
_selectState,
|
||||||
transactions => transactions.byId || {}
|
state => state.transactions
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectTransactionItems = createSelector(
|
export const selectTransactionItems = createSelector(
|
||||||
selectTransactionsById,
|
selectTransactionsById,
|
||||||
byId => {
|
byId => {
|
||||||
const transactionItems = [];
|
const items = [];
|
||||||
const txids = Object.keys(byId);
|
|
||||||
txids.forEach(txid => {
|
Object.keys(byId).forEach(txid => {
|
||||||
const tx = byId[txid];
|
const tx = byId[txid];
|
||||||
transactionItems.push({
|
|
||||||
id: txid,
|
//ignore dust/fees
|
||||||
|
if (Math.abs(tx.amount) === Math.abs(tx.fee)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let append = [];
|
||||||
|
|
||||||
|
append.push(
|
||||||
|
...tx.claim_info.map(item =>
|
||||||
|
Object.assign({}, tx, item, {
|
||||||
|
type: item.claim_name[0] === "@" ? "channel" : "publish",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
append.push(
|
||||||
|
...tx.support_info.map(item =>
|
||||||
|
Object.assign({}, tx, item, {
|
||||||
|
type: !item.is_tip ? "support" : "tip",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
append.push(
|
||||||
|
...tx.update_info.map(item =>
|
||||||
|
Object.assign({}, tx, item, { type: "update" })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!append.length) {
|
||||||
|
append.push(
|
||||||
|
Object.assign({}, tx, {
|
||||||
|
type: tx.value < 0 ? "spend" : "receive",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push(
|
||||||
|
...append.map(item => {
|
||||||
|
//value on transaction, amount on outpoint
|
||||||
|
//amount is always positive, but should match sign of value
|
||||||
|
const amount = parseFloat(
|
||||||
|
item.amount ? (item.value < 0 ? -1 : 1) * item.amount : item.value
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
txid: txid,
|
||||||
date: tx.timestamp ? new Date(parseInt(tx.timestamp) * 1000) : null,
|
date: tx.timestamp ? new Date(parseInt(tx.timestamp) * 1000) : null,
|
||||||
amount: parseFloat(tx.value),
|
amount: amount,
|
||||||
claim_info: tx.claim_info,
|
fee: amount < 0 ? -1 * tx.fee / append.length : 0,
|
||||||
support_info: tx.support_info,
|
claim_id: item.claim_id,
|
||||||
update_info: tx.update_info,
|
claim_name: item.claim_name,
|
||||||
fee: tx.fee,
|
type: item.type || "send",
|
||||||
|
nout: item.nout,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
return items.reverse();
|
||||||
return transactionItems.reverse();
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -61,6 +103,11 @@ export const selectIsFetchingTransactions = createSelector(
|
||||||
state => state.fetchingTransactions
|
state => state.fetchingTransactions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectIsSendingSupport = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => state.sendingSupport
|
||||||
|
);
|
||||||
|
|
||||||
export const selectReceiveAddress = createSelector(
|
export const selectReceiveAddress = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
state => state.receiveAddress
|
state => state.receiveAddress
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "lbry-web-ui",
|
"name": "lbry-web-ui",
|
||||||
"version": "0.15.1",
|
"version": "0.16.0",
|
||||||
"description": "LBRY UI",
|
"description": "LBRY UI",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
|
|
@ -3,6 +3,7 @@ html
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
@ -27,6 +28,11 @@ body
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--color-money);
|
color: var(--color-money);
|
||||||
}
|
}
|
||||||
|
.credit-amount--fee
|
||||||
|
{
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: var(--color-meta-light);
|
||||||
|
}
|
||||||
|
|
||||||
#main-content
|
#main-content
|
||||||
{
|
{
|
||||||
|
@ -50,7 +56,8 @@ body
|
||||||
width: $width-page-constrained;
|
width: $width-page-constrained;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
main.main--refreshing {
|
|
||||||
|
.reloading {
|
||||||
&:before {
|
&:before {
|
||||||
$width: 30px;
|
$width: 30px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -1656,3 +1656,9 @@
|
||||||
.icon-medium:before {
|
.icon-medium:before {
|
||||||
content: "\f23a";
|
content: "\f23a";
|
||||||
}
|
}
|
||||||
|
.icon-address-book:before {
|
||||||
|
content: "\f2b9";
|
||||||
|
}
|
||||||
|
.icon-envelope-open:before {
|
||||||
|
content: "\f2b6";
|
||||||
|
}
|
||||||
|
|
|
@ -90,8 +90,8 @@ $text-color: #000;
|
||||||
|
|
||||||
/* Tabs */
|
/* Tabs */
|
||||||
--tab-bg: transparent;
|
--tab-bg: transparent;
|
||||||
--tab-color: #666;
|
--tab-color: rgba(0, 0, 0, 0.5);
|
||||||
--tab-active-color: var(--header-active-color);
|
--tab-active-color: var(--color-primary);
|
||||||
--tab-border-size: 2px;
|
--tab-border-size: 2px;
|
||||||
--tab-border: var(--tab-border-size) solid var(--tab-active-color);
|
--tab-border: var(--tab-border-size) solid var(--tab-active-color);
|
||||||
|
|
||||||
|
@ -132,4 +132,7 @@ $text-color: #000;
|
||||||
--scrollbar-thumb-hover-bg: rgba(0, 0, 0, 0.35);
|
--scrollbar-thumb-hover-bg: rgba(0, 0, 0, 0.35);
|
||||||
--scrollbar-thumb-active-bg: var(--color-primary);
|
--scrollbar-thumb-active-bg: var(--color-primary);
|
||||||
--scrollbar-track-bg: transparent;
|
--scrollbar-track-bg: transparent;
|
||||||
|
|
||||||
|
/* Divider */
|
||||||
|
--divider: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,6 @@
|
||||||
@import "component/_pagination.scss";
|
@import "component/_pagination.scss";
|
||||||
@import "component/_markdown-editor.scss";
|
@import "component/_markdown-editor.scss";
|
||||||
@import "component/_scrollbar.scss";
|
@import "component/_scrollbar.scss";
|
||||||
|
@import "component/_tabs.scss";
|
||||||
@import "page/_developer.scss";
|
@import "page/_developer.scss";
|
||||||
@import "page/_show.scss";
|
@import "page/_show.scss";
|
||||||
|
|
|
@ -86,3 +86,9 @@ $button-focus-shift: 12%;
|
||||||
{
|
{
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button--submit {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
border-radius: var(--card-radius);
|
border-radius: var(--card-radius);
|
||||||
margin-bottom: var(--card-margin);
|
margin-bottom: var(--card-margin);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
|
//below added to prevent scrollbar on long titles when show page loads, would prefer a cleaner CSS solution
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
.card--obscured
|
.card--obscured
|
||||||
{
|
{
|
||||||
|
@ -35,6 +38,11 @@
|
||||||
margin-top: var(--card-margin);
|
margin-top: var(--card-margin);
|
||||||
margin-bottom: var(--card-margin);
|
margin-bottom: var(--card-margin);
|
||||||
}
|
}
|
||||||
|
.card__title-primary .meta {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
.card__title-identity {
|
.card__title-identity {
|
||||||
margin-top: $spacing-vertical * 1/3;
|
margin-top: $spacing-vertical * 1/3;
|
||||||
margin-bottom: $spacing-vertical * 1/3;
|
margin-bottom: $spacing-vertical * 1/3;
|
||||||
|
@ -57,6 +65,9 @@
|
||||||
.card__content {
|
.card__content {
|
||||||
margin-top: var(--card-margin);
|
margin-top: var(--card-margin);
|
||||||
margin-bottom: var(--card-margin);
|
margin-bottom: var(--card-margin);
|
||||||
|
table:not(:last-child) {
|
||||||
|
margin-bottom: var(--card-margin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$font-size-subtext-multiple: 0.82;
|
$font-size-subtext-multiple: 0.82;
|
||||||
.card__subtext {
|
.card__subtext {
|
||||||
|
|
|
@ -56,38 +56,3 @@
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nav.sub-header
|
|
||||||
{
|
|
||||||
text-transform: uppercase;
|
|
||||||
padding: 0 0 $spacing-vertical;
|
|
||||||
max-width: $width-page-constrained;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
> a
|
|
||||||
{
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 15px;
|
|
||||||
padding: 0 5px;
|
|
||||||
line-height:calc(var(--header-height) - $spacing-vertical - var(--tab-border-size));
|
|
||||||
color: var(--tab-color);
|
|
||||||
|
|
||||||
&:first-child
|
|
||||||
{
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
&:last-child
|
|
||||||
{
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
&.sub-header-selected
|
|
||||||
{
|
|
||||||
border-bottom: var(--tab-border);
|
|
||||||
color: var(--tab-active-color);
|
|
||||||
}
|
|
||||||
&:hover
|
|
||||||
{
|
|
||||||
color: var(--tab-active-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -60,3 +60,11 @@ table.table-standard {
|
||||||
table.table-stretch {
|
table.table-stretch {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.table-transactions {
|
||||||
|
td:nth-of-type(1) { width: 15%; }
|
||||||
|
td:nth-of-type(2) { width: 15%; }
|
||||||
|
td:nth-of-type(3) { width: 15%; }
|
||||||
|
td:nth-of-type(4) { width: 40%; }
|
||||||
|
td:nth-of-type(5) { width: 15%; }
|
||||||
|
}
|
57
ui/scss/component/_tabs.scss
Normal file
57
ui/scss/component/_tabs.scss
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/* Tabs */
|
||||||
|
|
||||||
|
nav.sub-header
|
||||||
|
{
|
||||||
|
text-transform: uppercase;
|
||||||
|
max-width: $width-page-constrained;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
border-bottom: var(--divider);
|
||||||
|
> a
|
||||||
|
{
|
||||||
|
height: 38px;
|
||||||
|
line-height: 38px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: baseline;
|
||||||
|
margin: 0 8px;
|
||||||
|
padding: 0 8px;
|
||||||
|
color: var(--tab-color);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:first-child
|
||||||
|
{
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
&:last-child
|
||||||
|
{
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
&.sub-header-selected
|
||||||
|
{
|
||||||
|
color: var(--tab-active-color);
|
||||||
|
&:before {
|
||||||
|
width: 100%;
|
||||||
|
height: var(--tab-border-size);
|
||||||
|
background: var(--tab-active-color);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
content: '';
|
||||||
|
animation-name: activeTab;
|
||||||
|
animation-duration: 0.3s;
|
||||||
|
animation-timing-function: cubic-bezier(.55,0,.1,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--tab-active-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes activeTab {
|
||||||
|
from {width: 0;}
|
||||||
|
to {width: 100%;}
|
||||||
|
}
|
11
ui/watch.bat
Normal file
11
ui/watch.bat
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
set found=
|
||||||
|
for %%F in (
|
||||||
|
"%~dp0\node_modules\node-sass\bin\node-sass"
|
||||||
|
"%~dp0\node_modules\.bin\webpack"
|
||||||
|
) do if exist %%F (set found=1)
|
||||||
|
if not defined found EXIT
|
||||||
|
|
||||||
|
node %~dp0\node_modules\node-sass\bin\node-sass --output %~dp0\..\app\dist\css --sourcemap=none %~dp0\scss
|
||||||
|
%~dp0\node_modules\.bin\webpack --config %~dp0\webpack.dev.config.js --progress --colors --watch
|
Loading…
Add table
Reference in a new issue