Merge remote-tracking branch 'upstream/master' into tb2

This commit is contained in:
hackrush 2017-08-29 13:21:23 +05:30
commit 21175ba608
136 changed files with 1293 additions and 23087 deletions

View file

@ -17,6 +17,10 @@ values =
production
[bumpversion:file:app/package.json]
search = "version": "{current_version}"
replace = "version": "{new_version}"
[bumpversion:file:ui/package.json]
search = "version": "{current_version}"
replace = "version": "{new_version}"

View file

@ -31,7 +31,7 @@ Tell us what happens instead
You can include a screenshot instead of typing it out -->
<!-- For the daemon, run:
curl 'http://localhost:5279/lbryapi' --data '{"method":"version"}'
curl 'http://localhost:5279' --data '{"method":"version"}'
and include the full output -->
- LBRY Daemon version:

2
.gitignore vendored
View file

@ -10,8 +10,10 @@
/app/dist
/app/locales
/app/node_modules
/build/venv
/build/daemon.ver
/lbry-app-venv
/lbry-app
/lbry-venv

View file

@ -8,20 +8,32 @@ Web UI version numbers should always match the corresponding version of LBRY App
## [Unreleased]
### Added
* Added a new component, `FormFieldPrice` which is now used in Publish and Settings
* Added a tipping button to send LBRY Credits to the publisher
*
* Added a forward button and improved history behavior. Back/forward disable when unusable.
* Added a new component, `FormFieldPrice` which is now used in Publish and Settings.
* Added wallet backup guide reference.
### Changed
* Some form field refactoring as we progress towards form sanity.
* Updated to daemon [0.15](https://github.com/lbryio/lbry/releases). Most relevant changes for app are improved announcing of content and a fix for the daemon getting stuck running.
* Continued to refine first-run process, process for new users, and introducing people to LBRY and LBRY credits.
* Changed the default price settings.
* When an "Open" button is clicked on a show page, if the file fails to open, the app will try to open the file's folder.
*
* Some form field refactoring as we take baby steps towards form sanity.
* Replaced confusing placeholder text from email input.
* Refactored modal and settings logic.
* Updated several packages and fixed warnings in build process (all but the [fsevents warning](https://github.com/yarnpkg/yarn/issues/3738), which is a rather dramatic debate)
### Fixed
* Tiles will no longer be blurry on hover (Windows only bug)
* Removed placeholder values from price selection form fields, which was causing confusion that these were real values (#426)
* Fixed showing "other currency" help tip in publish form, which was caused due to not "setting" state for price
*
* Public page now properly checks for all required fields are filled
* Fixed pagination styling for pages > 5 (#416)
* Fixed sizing on squat videos (#419)
* Support claims no longer show up on Published page (#384)
* Fixed rendering of small prices (#461)
* Fixed incorrect URI in Downloads/Published page (#460)
* Fixed menu bug (#503)
### Deprecated
*

View file

@ -22,10 +22,12 @@ To install from source or make changes to the application, continue reading belo
### One-time Setup
1. Install node and npm.
2. Check out this repo.
3. Set up a Python virtual environment, or live on the wild side.
4. Run `./build.sh`. This builds the UI assets and puts them into `app/dist`. It also downloads [lbry daemon](https://github.com/lbryio/lbry/releases).
1. Install npm and node (v6 and above required, use [nvm](https://github.com/creationix/nvm/blob/master/README.md) if having trouble)
2. Install keytar and libsecret (see [keytar repository](https://github.com/atom/node-keytar) )
3. Install yarn by running: npm install -g yarn (may require elevated permissions)
4. Check out this repo.
5. Set up a Python virtual environment, or live on the wild side.
6. Run `./build.sh`. This builds the UI assets and puts them into `app/dist`. It also downloads [lbry daemon](https://github.com/lbryio/lbry/releases).
### Running
@ -51,4 +53,4 @@ checkout out the build steps in [appveyor.yml](https://github.com/lbryio/lbry-ap
## Internationalization
If you want to help translating the lbry-app, you can copy the en.json file in /app/locales and modify the values while leaving the keys as their original English strings. An example for this would be: `"Skip": "Überspringen",` Translations should automatically show up in options.
If you want to help translating the lbry-app, you can copy the en.json file in /app/locales and modify the values while leaving the keys as their original English strings. An example for this would be: `"Skip": "Überspringen",` Translations should automatically show up in options.

View file

@ -32,7 +32,7 @@ const DAEMON_PATH = process.env.LBRY_DAEMON || path.join(__dirname, 'dist', 'lbr
let client = jayson.client.http({
host: 'localhost',
port: 5279,
path: '/lbryapi',
path: '/',
timeout: 1000
});

View file

@ -20,6 +20,8 @@
"electron-rebuild": "^1.5.11"
},
"lbrySettings": {
"lbrynetDaemonVersion": "0.14.2"
}
"lbrynetDaemonVersion": "0.15.1",
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-daemon-vDAEMONVER-OSNAME.zip"
},
"license": "MIT"
}

View file

@ -1 +0,0 @@
https://github.com/lbryio/lbry/releases/download/v0.14.2/lbrynet-daemon-v0.14.2-OSNAME.zip

View file

@ -29,7 +29,10 @@ cd ..
# get daemon and cli executable
$daemon_url = (Get-Content build\DAEMON_URL -Raw).replace("OSNAME", "windows")
$package_settings = (Get-Content app\package.json -Raw | ConvertFrom-Json).lbrySettings
$daemon_ver = $package_settings.lbrynetDaemonVersion
$daemon_url_template = $package_settings.lbrynetDaemonUrlTemplate
$daemon_url = $daemon_url_template.Replace('OSNAME', 'windows').Replace('DAEMONVER', $daemon_ver)
Invoke-WebRequest -Uri $daemon_url -OutFile daemon.zip
Expand-Archive daemon.zip -DestinationPath app\dist\
dir app\dist\ # verify that daemon binary is there

View file

@ -80,10 +80,19 @@ else
OSNAME="linux"
fi
DAEMON_VER=$(node -e "console.log(require(\"$ROOT/app/package.json\").lbrySettings.lbrynetDaemonVersion)")
DAEMON_URL="https://github.com/lbryio/lbry/releases/download/v${DAEMON_VER}/lbrynet-daemon-v${DAEMON_VER}-${OSNAME}.zip"
wget --quiet "$DAEMON_URL" -O "$BUILD_DIR/daemon.zip"
unzip "$BUILD_DIR/daemon.zip" -d "$ROOT/app/dist/"
rm "$BUILD_DIR/daemon.zip"
DAEMON_URL_TEMPLATE=$(node -e "console.log(require(\"$ROOT/app/package.json\").lbrySettings.lbrynetDaemonUrlTemplate)")
DAEMON_URL=$(echo ${DAEMON_URL_TEMPLATE//DAEMONVER/$DAEMON_VER} | sed "s/OSNAME/$OSNAME/g")
DAEMON_VER_PATH="$BUILD_DIR/daemon.ver"
echo "$DAEMON_VER_PATH"
if [[ ! -f $DAEMON_VER_PATH || ! -f $ROOT/app/dist/lbrynet-daemon || "$(< "$DAEMON_VER_PATH")" != "$DAEMON_VER" ]]; then
wget --quiet "$DAEMON_URL" -O "$BUILD_DIR/daemon.zip"
unzip "$BUILD_DIR/daemon.zip" -d "$ROOT/app/dist/"
rm "$BUILD_DIR/daemon.zip"
echo "$DAEMON_VER" > "$DAEMON_VER_PATH"
else
echo "Already have daemon version $DAEMON_VER, skipping download"
fi

View file

@ -5,6 +5,7 @@ This script should be run locally, not on a build server.
import argparse
import contextlib
import os
import json
import re
import requests
import subprocess
@ -16,7 +17,7 @@ import github
import changelog
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
DAEMON_URL_FILE = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'DAEMON_URL')
APP_PACKAGE_JSON_FILE = os.path.join(ROOT, 'app', 'package.json')
def main():
@ -38,11 +39,11 @@ def main():
print 'Current version: {}'.format(repo.current_version)
print 'New version: {}'.format(repo.new_version)
with open(DAEMON_URL_FILE, 'r') as f:
daemon_url_template = f.read().strip()
daemon_version = re.search('/(?P<version>v[^/]+)', daemon_url_template)
print 'Daemon version: {} ({})'.format(
daemon_version.group('version'), daemon_url_template)
with open(APP_PACKAGE_JSON_FILE, 'r') as f:
package_settings = json.load(f)['lbrySettings']
daemon_url_template = package_settings['lbrynetDaemonUrlTemplate']
daemon_version = package_settings['lbrynetDaemonVersion']
print 'Daemon version: {} ({})'.format(daemon_version, daemon_url_template.replace('DAEMONVER', daemon_version))
if not args.confirm and not confirm():
print "Aborting"
@ -190,18 +191,25 @@ def run_sanity_checks(repo, branch):
def check_daemon_urls():
success = True
with open(DAEMON_URL_FILE, 'r') as f:
daemon_url_template = f.read().strip()
if "OSNAME" not in daemon_url_template:
print "Daemon URL must include the string 'OSNAME'"
return False
for osname in ('linux', 'macos', 'windows'):
if not check_url(daemon_url_template.replace('OSNAME', osname)):
success = False
print "Daemon URL for " + osname + " does not work"
return success
with open(APP_PACKAGE_JSON_FILE, 'r') as f:
package_settings = json.load(f)['lbrySettings']
daemon_url_template = package_settings['lbrynetDaemonUrlTemplate']
daemon_version = package_settings['lbrynetDaemonVersion']
if "OSNAME" not in daemon_url_template:
print "Daemon URL must include the string \"OSNAME\""
return False
elif "DAEMONVER" not in daemon_url_template:
print "Daemon URL must include the string \"DAEMONVER\""
return False
for osname in ('linux', 'macos', 'windows'):
if not check_url(daemon_url_template.replace('DAEMONVER', daemon_version).replace('OSNAME', osname)):
print "Daemon URL for", osname, " does not work"
return False
return True
def check_url(url):
url = url.strip()

View file

@ -60,5 +60,6 @@
"electron": "^1.7.5",
"electron-builder": "^11.7.0",
"electron-debug": "^1.4.0"
}
},
"license": "MIT"
}

3
ui/dist/index.html vendored
View file

@ -4,7 +4,6 @@
<meta name="viewport" content="width=device-width">
<title>LBRY</title>
<link href='https://fonts.googleapis.com/css?family=Raleway:600,300' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
<link rel="icon" type="image/png" href="./img/fav/favicon-32x32.png" sizes="32x32">
@ -19,8 +18,6 @@
</head>
<body>
<div id="canvas"></div>
<script src="./js/mediaelement/jquery.js"></script>
<script src="./js/mediaelement/mediaelement-and-player.js"></script>
<script src="./js/bundle.js"></script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -1,14 +0,0 @@
<?xml version="1.0" standalone="no"?>
<svg id="bigplay" viewBox="0 0 100 200" style="background-color:#ffffff00" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
x="0px" y="0px" width="100px" height="200px"
>
<g id="dark">
<path id="Polygon" d="M 72.5 49.5 L 38.75 68.9856 L 38.75 30.0144 L 72.5 49.5 Z" fill="#ffffff" opacity="0.75" />
<path id="Ellipse" d="M 13 50.5 C 13 29.7891 29.7891 13 50.5 13 C 71.2109 13 88 29.7891 88 50.5 C 88 71.2109 71.2109 88 50.5 88 C 29.7891 88 13 71.2109 13 50.5 Z" stroke="#ffffff" stroke-width="5" fill="none" opacity="0.75"/>
</g>
<g id="light">
<path id="Polygon2" d="M 72.5 149.5 L 38.75 168.9856 L 38.75 130.0144 L 72.5 149.5 Z" fill="#ffffff" opacity="1.0" />
<path id="Ellipse2" d="M 13 150.5 C 13 129.7891 29.7891 113 50.5 113 C 71.2109 113 88 129.7891 88 150.5 C 88 171.211 71.2109 188 50.5 188 C 29.7891 188 13 171.211 13 150.5 Z" stroke="#ffffff" stroke-width="5" fill="none" opacity="1.0"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,289 +0,0 @@
/* TED player */
.mejs-container.mejs-ted {
}
.mejs-ted .mejs-controls {
background: #eee;
height: 65px;
}
.mejs-ted .mejs-button,
.mejs-ted .mejs-time {
position: absolute;
background: #ddd;
}
.mejs-ted .mejs-controls .mejs-time-rail .mejs-time-total {
background-color: none;
background: url(controls-ted.png) repeat-x 0 -52px;
height: 6px;
}
.mejs-ted .mejs-controls .mejs-time-rail .mejs-time-buffering {
height: 6px;
}
.mejs-ted .mejs-controls .mejs-time-rail .mejs-time-loaded {
background-color: none;
background: url(controls-ted.png) repeat-x 0 -52px;
width: 0;
height: 6px;
}
.mejs-ted .mejs-controls .mejs-time-rail .mejs-time-current {
width: 0;
height: 6px;
background-color: none;
background: url(controls-ted.png) repeat-x 0 -59px;
}
.mejs-ted .mejs-controls .mejs-time-rail .mejs-time-handle {
display: block;
margin: 0;
width: 14px;
height: 21px;
top: -7px;
border: 0;
background: url(controls-ted.png) no-repeat 0 0;
}
.mejs-ted .mejs-controls .mejs-time-rail .mejs-time-float {
display: none;
}
.mejs-ted .mejs-controls .mejs-playpause-button {
top: 29px;
left: 9px;
width: 49px;
height: 28px;
}
.mejs-ted .mejs-controls .mejs-playpause-button button {
width: 49px;
height: 28px;
background: url(controls-ted.png) no-repeat -50px -23px;
margin: 0;
padding: 0;
}
.mejs-ted .mejs-controls .mejs-pause button {
background-position: 0 -23px;
}
.mejs-ted .mejs-controls .mejs-fullscreen-button {
top: 34px;
right: 9px;
width: 17px;
height: 15px;
background : none;
}
.mejs-ted .mejs-controls .mejs-fullscreen-button button {
width: 19px;
height: 17px;
background: transparent url(controls-ted.png) no-repeat 0 -66px;
margin: 0;
padding: 0;
}
.mejs-ted .mejs-controls .mejs-unfullscreen button {
background: transparent url(controls-ted.png) no-repeat -21px -66px;
margin: 0;
padding: 0;
}
.mejs-ted .mejs-controls .mejs-volume-button {
top: 30px;
right: 35px;
width: 24px;
height: 22px;
}
.mejs-ted .mejs-controls .mejs-mute button {
background: url(controls-ted.png) no-repeat -15px 0;
width: 24px;
height: 22px;
margin: 0;
padding: 0;
}
.mejs-ted .mejs-controls .mejs-unmute button {
background: url(controls-ted.png) no-repeat -40px 0;
width: 24px;
height: 22px;
margin: 0;
padding: 0;
}
.mejs-ted .mejs-controls .mejs-volume-button .mejs-volume-slider {
background: #fff;
border: solid 1px #aaa;
border-width: 1px 1px 0 1px;
width: 22px;
height: 65px;
top: -65px;
}
.mejs-ted .mejs-controls .mejs-volume-button .mejs-volume-total {
background: url(controls-ted.png) repeat-y -41px -66px;
left: 8px;
width: 6px;
height: 50px;
}
.mejs-ted .mejs-controls .mejs-volume-button .mejs-volume-current {
left: 8px;
width: 6px;
background: url(controls-ted.png) repeat-y -48px -66px;
height: 50px;
}
.mejs-ted .mejs-controls .mejs-volume-button .mejs-volume-handle {
display: none;
}
.mejs-ted .mejs-controls .mejs-time span {
color: #333;
}
.mejs-ted .mejs-controls .mejs-currenttime-container {
position: absolute;
top: 32px;
right: 100px;
border: solid 1px #999;
background: #fff;
color: #333;
padding-top: 2px;
border-radius: 3px;
color: #333;
}
.mejs-ted .mejs-controls .mejs-duration-container {
position: absolute;
top: 32px;
right: 65px;
border: solid 1px #999;
background: #fff;
color: #333;
padding-top: 2px;
border-radius: 3px;
color: #333;
}
.mejs-ted .mejs-controls .mejs-time button{
color: #333;
}
.mejs-ted .mejs-controls .mejs-captions-button {
display: none;
}
/* END: TED player */
/* WMP player */
.mejs-container.mejs-wmp {
}
.mejs-wmp .mejs-controls {
background: transparent url(controls-wmp-bg.png) center 16px no-repeat;
height: 65px;
}
.mejs-wmp .mejs-button,
.mejs-wmp .mejs-time {
position: absolute;
background: transparent;
}
.mejs-wmp .mejs-controls .mejs-time-rail .mejs-time-total {
background-color: transparent;
border: solid 1px #ccc;
height: 3px;
}
.mejs-wmp .mejs-controls .mejs-time-rail .mejs-time-buffering {
height: 3px;
}
.mejs-wmp .mejs-controls .mejs-time-rail .mejs-time-loaded {
background-color: rgba(255,255,255,0.3);
width: 0;
height: 3px;
}
.mejs-wmp .mejs-controls .mejs-time-rail .mejs-time-current {
width: 0;
height: 1px;
background-color: #014CB6;
border: solid 1px #7FC9FA;
border-width: 1px 0;
border-color: #7FC9FA #fff #619FF2 #fff;
}
.mejs-wmp .mejs-controls .mejs-time-rail .mejs-time-handle {
display: block;
margin: 0;
width: 16px;
height: 9px;
top: -3px;
border: 0;
background: url(controls-wmp.png) no-repeat 0 -80px;
}
.mejs-wmp .mejs-controls .mejs-time-rail .mejs-time-float {
display: none;
}
.mejs-wmp .mejs-controls .mejs-playpause-button {
top: 10px;
left: 50%;
margin: 10px 0 0 -20px;
width: 40px;
height: 40px;
}
.mejs-wmp .mejs-controls .mejs-playpause-button button {
width: 40px;
height: 40px;
background: url(controls-wmp.png) no-repeat 0 0;
margin: 0;
padding: 0;
}
.mejs-wmp .mejs-controls .mejs-pause button {
background-position: 0 -40px;
}
.mejs-wmp .mejs-controls .mejs-currenttime-container {
position: absolute;
top: 25px;
left: 50%;
margin-left: -93px;
}
.mejs-wmp .mejs-controls .mejs-duration-container {
position: absolute;
top: 25px;
left: 50%;
margin-left: -58px;
}
.mejs-wmp .mejs-controls .mejs-volume-button {
top: 32px;
right: 50%;
margin-right: -55px;
width: 20px;
height: 15px;
}
.mejs-wmp .mejs-controls .mejs-volume-button button {
margin: 0;
padding: 0;
background: url(controls-wmp.png) no-repeat -42px -17px;
width: 20px;
height: 15px;
}
.mejs-wmp .mejs-controls .mejs-unmute button {
margin: 0;
padding: 0;
background: url(controls-wmp.png) no-repeat -42px 0;
width: 20px;
height: 15px;
}
.mejs-wmp .mejs-controls .mejs-volume-button .mejs-volume-slider {
background: rgba(102,102,102,0.6);
}
.mejs-wmp .mejs-controls .mejs-fullscreen-button {
top: 32px;
right: 50%;
margin-right: -82px;
width: 15px;
height: 14px;
}
.mejs-wmp .mejs-controls .mejs-fullscreen-button button {
margin: 0;
padding: 0;
background: url(controls-wmp.png) no-repeat -63px 0;
width: 15px;
height: 14px;
}
.mejs-wmp .mejs-controls .mejs-captions-button {
display: none;
}
/* END: WMP player */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

3
ui/dist/quit.html vendored
View file

@ -3,8 +3,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>LBRY</title>
<link href='https://fonts.googleapis.com/css?family=Raleway:600,300' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
<link href="./js/mediaelement/mediaelementplayer.css" rel="stylesheet" type="text/css" />

View file

@ -3,11 +3,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>LBRY</title>
<link href='https://fonts.googleapis.com/css?family=Raleway:600,300' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
<link href="./js/mediaelement/mediaelementplayer.css" rel="stylesheet" type="text/css" />
<link rel="icon" type="image/png" href="./img/fav/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="./img/fav/favicon-194x194.png" sizes="194x194">
<link rel="icon" type="image/png" href="./img/fav/favicon-96x96.png" sizes="96x96">

View file

@ -3,11 +3,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>LBRY</title>
<link href='https://fonts.googleapis.com/css?family=Raleway:600,300' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
<link href="./js/mediaelement/mediaelementplayer.css" rel="stylesheet" type="text/css" />
<link rel="icon" type="image/png" href="./img/fav/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="./img/fav/favicon-194x194.png" sizes="194x194">
<link rel="icon" type="image/png" href="./img/fav/favicon-96x96.png" sizes="96x96">

View file

@ -8,6 +8,8 @@ import {
selectPageTitle,
selectCurrentPage,
selectCurrentParams,
selectHistoryBack,
selectHistoryForward,
} from "selectors/app";
import { doSearch } from "actions/search";
import { doFetchDaemonSettings } from "actions/settings";
@ -31,7 +33,11 @@ export function doNavigate(path, params = {}, options = {}) {
const state = getState();
const pageTitle = selectPageTitle(state);
dispatch(doHistoryPush({ params }, pageTitle, url));
const historyState = history.state;
dispatch(
doHistoryPush({ params, page: historyState.page + 1 }, pageTitle, url)
);
};
}
@ -77,10 +83,35 @@ export function doChangePath(path, options = {}) {
export function doHistoryBack() {
return function(dispatch, getState) {
if (!history.state) return;
if (history.state.index === 0) return;
// Get back history from stack
const back = selectHistoryBack(getState());
history.back();
if (back) {
// Set location
dispatch(doChangePath(back.location));
dispatch({
type: types.HISTORY_NAVIGATE,
data: { page: back },
});
}
};
}
export function doHistoryForward() {
return function(dispatch, getState) {
// Get forward history from stack
const forward = selectHistoryForward(getState());
if (forward) {
// Set location
dispatch(doChangePath(forward.location));
dispatch({
type: types.HISTORY_NAVIGATE,
data: { page: forward },
});
}
};
}
@ -88,6 +119,12 @@ export function doHistoryPush(currentState, title, relativeUrl) {
return function(dispatch, getState) {
title += " - LBRY";
history.pushState(currentState, title, `#${relativeUrl}`);
dispatch({
type: types.HISTORY_NAVIGATE,
data: {
location: relativeUrl,
},
});
};
}
@ -266,10 +303,22 @@ export function doDaemonReady() {
return function(dispatch, getState) {
const path = window.location.hash || "#/discover";
const params = parseQueryParams(path.split("?")[1] || "");
history.replaceState({ params, index: 0 }, document.title, `${path}`);
// Get first page
const page = {
index: 0,
location: path.replace(/^#/, ""),
};
history.replaceState(
{ params, is_first_page: true, page: 1 },
document.title,
`${path}`
);
dispatch(doAuthenticate());
dispatch({
type: types.DAEMON_READY,
data: { page },
});
dispatch(doFetchDaemonSettings());
dispatch(doFileList());
@ -302,3 +351,14 @@ export function doQuit() {
remote.app.quit();
};
}
export function doChangeVolume(volume) {
return function(dispatch, getState) {
dispatch({
type: types.VOLUME_CHANGED,
data: {
volume,
},
});
};
}

View file

@ -231,14 +231,15 @@ export function doStartDownload(uri, outpoint) {
return function(dispatch, getState) {
const state = getState();
if (!outpoint) { throw new Error("outpoint is required to begin a download"); }
if (!outpoint) {
throw new Error("outpoint is required to begin a download");
}
const { downloadingByOutpoint = {} } = state.fileInfo;
if (downloadingByOutpoint[outpoint]) return;
lbry.file_list({ outpoint, full_status: true }).then(([fileInfo]) => {
dispatch({
type: types.DOWNLOADING_STARTED,
data: {
@ -282,29 +283,32 @@ export function doLoadVideo(uri) {
},
});
lbry.get({ uri }).then(streamInfo => {
const timeout =
streamInfo === null ||
typeof streamInfo !== "object" ||
streamInfo.error == "Timeout";
lbry
.get({ uri })
.then(streamInfo => {
const timeout =
streamInfo === null ||
typeof streamInfo !== "object" ||
streamInfo.error == "Timeout";
if (timeout) {
if (timeout) {
dispatch({
type: types.LOADING_VIDEO_FAILED,
data: { uri },
});
dispatch(doOpenModal("timedOut"));
} else {
dispatch(doDownloadFile(uri, streamInfo));
}
})
.catch(error => {
dispatch({
type: types.LOADING_VIDEO_FAILED,
data: { uri },
});
dispatch(doOpenModal("timedOut"));
} else {
dispatch(doDownloadFile(uri, streamInfo));
}
}).catch(error => {
dispatch({
type: types.LOADING_VIDEO_FAILED,
data: { uri },
dispatch(doAlertError(error));
});
dispatch(doAlertError(error));
});
};
}
@ -336,7 +340,7 @@ export function doPurchaseUri(uri, purchaseModalName) {
const { cost } = costInfo;
// the file is free or we have partially downloaded it
if (cost <= 0.01 || (fileInfo && fileInfo.download_directory)) {
if (cost === 0 || (fileInfo && fileInfo.download_directory)) {
dispatch(doLoadVideo(uri));
return Promise.resolve();
}
@ -361,9 +365,6 @@ export function doFetchClaimsByChannel(uri, page) {
lbry.claim_list_by_channel({ uri, page }).then(result => {
const claimResult = result[uri],
claims = claimResult ? claimResult.claims_in_channel : [],
totalPages = claimResult
? claimResult.claims_in_channel_pages
: undefined,
currentPage = claimResult ? claimResult.returned_page : undefined;
dispatch({
@ -371,7 +372,6 @@ export function doFetchClaimsByChannel(uri, page) {
data: {
uri,
claims,
totalPages,
page: currentPage,
},
});
@ -379,6 +379,28 @@ export function doFetchClaimsByChannel(uri, page) {
};
}
export function doFetchClaimCountByChannel(uri) {
return function(dispatch, getState) {
dispatch({
type: types.FETCH_CHANNEL_CLAIM_COUNT_STARTED,
data: { uri },
});
lbry.claim_list_by_channel({ uri }).then(result => {
const claimResult = result[uri],
totalClaims = claimResult ? claimResult.claims_in_channel : 0;
dispatch({
type: types.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED,
data: {
uri,
totalClaims,
},
});
});
};
}
export function doFetchClaimListMine() {
return function(dispatch, getState) {
dispatch({

View file

@ -1,5 +1,9 @@
import * as types from "constants/action_types";
import * as settings from "constants/settings";
import batchActions from "util/batchActions";
import lbry from "lbry";
import fs from "fs";
import http from "http";
export function doFetchDaemonSettings() {
return function(dispatch, getState) {
@ -41,3 +45,96 @@ export function doSetClientSetting(key, value) {
},
};
}
export function doDownloadLanguage(langFile) {
return function(dispatch, getState) {
const destinationPath = `app/locales/${langFile}`;
const language = langFile.replace(".json", "");
const req = http.get(
{
headers: {
"Content-Type": "text/html",
},
host: "i18n.lbry.io",
path: `/langs/${langFile}`,
},
response => {
if (response.statusCode === 200) {
const file = fs.createWriteStream(destinationPath);
file.on("error", errorHandler);
file.on("finish", () => {
file.close();
// push to our local list
dispatch({
type: types.DOWNLOAD_LANGUAGE_SUCCEEDED,
data: { language: language },
});
});
response.pipe(file);
} else {
errorHandler(new Error("Language request failed."));
}
}
);
const errorHandler = err => {
fs.unlink(destinationPath, () => {}); // Delete the file async. (But we don't check the result)
dispatch({
type: types.DOWNLOAD_LANGUAGE_FAILED,
data: { language },
});
};
req.setTimeout(30000, function() {
req.abort();
});
req.on("error", errorHandler);
req.end();
};
}
export function doDownloadLanguages() {
return function(dispatch, getState) {
if (!fs.existsSync(app.i18n.directory)) {
fs.mkdirSync(app.i18n.directory);
}
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
try {
const files = JSON.parse(xhr.responseText);
const actions = [];
files.forEach(file => {
actions.push(doDownloadLanguage(file));
});
dispatch(batchActions(...actions));
} catch (err) {
throw err;
}
} else {
throw new Error(
__("The list of available languages could not be retrieved.")
);
}
}
};
xhr.open("get", "http://i18n.lbry.io");
xhr.send();
};
}
export function doChangeLanguage(language) {
return function(dispatch, getState) {
lbry.setClientSetting(settings.LANGUAGE, language);
app.i18n.setLocale(language);
};
}

View file

@ -1,7 +1,6 @@
import * as types from "constants/action_types";
import * as modals from "constants/modal_types";
import lbryio from "lbryio";
import { setLocal } from "utils";
import { doOpenModal } from "actions/app";
import { doRewardList, doClaimRewardType } from "actions/rewards";
import { selectEmailToVerify, selectUser } from "selectors/user";
@ -95,15 +94,6 @@ export function doUserEmailNew(email) {
};
}
export function doUserEmailDecline() {
return function(dispatch, getState) {
setLocal("user_email_declined", true);
dispatch({
type: types.USER_EMAIL_DECLINE,
});
};
}
export function doUserEmailVerify(verificationToken) {
return function(dispatch, getState) {
const email = selectEmailToVerify(getState());

View file

@ -111,7 +111,7 @@ export function doSendDraftTransaction() {
};
lbry
.send_amount_to_address({
.wallet_send({
amount: draftTx.amount,
address: draftTx.address,
})

View file

@ -1,12 +1,13 @@
import store from "store.js";
import lbry from "./lbry.js";
import * as settings from "constants/settings";
const env = ENV;
const config = {
...require(`./config/${env}`),
};
const language = lbry.getClientSetting("language")
? lbry.getClientSetting("language")
const language = lbry.getClientSetting(settings.LANGUAGE)
? lbry.getClientSetting(settings.LANGUAGE)
: "en";
const i18n = require("y18n")({
directory: "app/locales",

View file

@ -1,17 +1,8 @@
import React from "react";
import Router from "component/router";
import Router from "component/router/index";
import Header from "component/header";
import ModalError from "component/modalError";
import ModalAuthFailure from "component/modalAuthFailure";
import ModalDownloading from "component/modalDownloading";
import ModalInsufficientCredits from "component/modalInsufficientCredits";
import ModalUpgrade from "component/modalUpgrade";
import ModalWelcome from "component/modalWelcome";
import ModalFirstReward from "component/modalFirstReward";
import ModalTransactionFailed from "component/modalTransactionFailed";
import ModalInsufficientBalance from "component/modalInsufficientBalance";
import ModalRouter from "modal/modalRouter";
import lbry from "lbry";
import * as modals from "constants/modal_types";
class App extends React.PureComponent {
componentWillMount() {
@ -36,52 +27,23 @@ class App extends React.PureComponent {
fetchRewardedContent();
this.showWelcome(this.props);
this.scrollListener = () => this.props.recordScroll(window.scrollY);
window.addEventListener("scroll", this.scrollListener);
}
componentWillReceiveProps(nextProps) {
this.showWelcome(nextProps);
}
showWelcome(props) {
const { isWelcomeAcknowledged, openWelcomeModal, user } = props;
if (
!isWelcomeAcknowledged &&
user &&
!user.is_reward_approved &&
!user.is_identity_verified
) {
openWelcomeModal();
}
}
componentWillUnmount() {
window.removeEventListener("scroll", this.scrollListener);
}
render() {
const { modal } = this.props;
return (
<div id="window">
<Header />
<div id="main-content">
<Router />
</div>
{modal == modals.UPGRADE && <ModalUpgrade />}
{modal == modals.DOWNLOADING && <ModalDownloading />}
{modal == modals.ERROR && <ModalError />}
{modal == modals.INSUFFICIENT_CREDITS && <ModalInsufficientCredits />}
{modal == modals.WELCOME && <ModalWelcome />}
{modal == modals.FIRST_REWARD && <ModalFirstReward />}
{modal == modals.AUTHENTICATION_FAILURE && <ModalAuthFailure />}
{modal == modals.TRANSACTION_FAILED && <ModalTransactionFailed />}
{modal == modals.INSUFFICIENT_BALANCE && <ModalInsufficientBalance />}
<ModalRouter />
</div>
);
}

View file

@ -1,5 +1,5 @@
import React from "react";
import { formatCredits } from "utils";
import { formatCredits, formatFullPrice } from "util/formatCredits";
import lbry from "../lbry.js";
//component/icon.js
@ -68,35 +68,50 @@ export class CreditAmount extends React.PureComponent {
isEstimate: React.PropTypes.bool,
label: React.PropTypes.bool,
showFree: React.PropTypes.bool,
showFullPrice: React.PropTypes.bool,
look: React.PropTypes.oneOf(["indicator", "plain"]),
};
static defaultProps = {
precision: 1,
precision: 2,
label: true,
showFree: false,
look: "indicator",
showFree: false,
showFullPrice: false,
};
render() {
const formattedAmount = formatCredits(
this.props.amount,
this.props.precision
);
const minimumRenderableAmount = Math.pow(10, -1 * this.props.precision);
const { amount, precision, showFullPrice } = this.props;
let formattedAmount;
let fullPrice = formatFullPrice(amount, 2);
if (showFullPrice) {
formattedAmount = fullPrice;
} else {
formattedAmount = amount > 0 && amount < minimumRenderableAmount
? "<" + minimumRenderableAmount
: formatCredits(amount, precision);
}
let amountText;
if (this.props.showFree && parseFloat(formattedAmount) == 0) {
if (this.props.showFree && parseFloat(this.props.amount) === 0) {
amountText = __("free");
} else if (this.props.label) {
amountText =
formattedAmount +
" " +
(parseFloat(formattedAmount) == 1 ? __("credit") : __("credits"));
(parseFloat(amount) == 1 ? __("credit") : __("credits"));
} else {
amountText = formattedAmount;
}
return (
<span className={`credit-amount credit-amount--${this.props.look}`}>
<span
className={`credit-amount credit-amount--${this.props.look}`}
title={fullPrice}
>
<span>
{amountText}
</span>

View file

@ -1,11 +1,11 @@
import React from "react";
import { Icon, BusyMessage } from "component/common";
import FilePrice from "component/filePrice";
import { Modal } from "component/modal";
import { Modal } from "modal/modal";
import Link from "component/link";
import { ToolTip } from "component/tooltip";
import { DropDownMenu, DropDownMenuItem } from "component/menu";
import ModalRemoveFile from "component/modalRemoveFile";
import ModalRemoveFile from "modal/modalRemoveFile";
import * as modals from "constants/modal_types";
class FileActions extends React.PureComponent {
@ -200,7 +200,7 @@ class FileActions extends React.PureComponent {
>
{__("This will purchase")} <strong>{title}</strong> {__("for")}{" "}
<strong>
<FilePrice uri={uri} look="plain" />
<FilePrice uri={uri} showFullPrice={true} look="plain" />
</strong>{" "}
{__("credits")}.
</Modal>
@ -209,7 +209,8 @@ class FileActions extends React.PureComponent {
contentLabel={__("Download failed")}
onConfirmed={closeModal}
>
{__("LBRY was unable to download the stream")} <strong>{uri}</strong>.
{__("LBRY was unable to download the stream")}{" "}{" "}
<strong>{title}</strong>.
</Modal>
{modal == modals.CONFIRM_FILE_REMOVE &&
<ModalRemoveFile

View file

@ -62,7 +62,8 @@ class FileCard extends React.PureComponent {
? metadata.thumbnail
: null;
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
const isRewardContent =
claim && rewardedContentClaimIds.includes(claim.claim_id);
let description = "";
if (isResolvingUri && !claim) {
@ -95,8 +96,9 @@ class FileCard extends React.PureComponent {
<div className="card__subtitle">
<span style={{ float: "right" }}>
<FilePrice uri={uri} />
{isRewardContent && <span>{" "}<IconFeatured /></span> }
{fileInfo && <span>{" "}<Icon fixed icon="icon-folder" /></span> }
{isRewardContent && <span>{" "}<IconFeatured /></span>}
{fileInfo &&
<span>{" "}<Icon fixed icon="icon-folder" /></span>}
</span>
<UriIndicator uri={uri} />
</div>

View file

@ -1,12 +1,8 @@
import React from "react";
import lbry from "lbry.js";
import lbryuri from "lbryuri.js";
import Link from "component/link";
import FormField from "component/formField";
import FileTile from "component/fileTile";
import rewards from "rewards.js";
import lbryio from "lbryio.js";
import { BusyMessage, Thumbnail } from "component/common.js";
import { BusyMessage } from "component/common.js";
class FileList extends React.PureComponent {
constructor(props) {
@ -55,6 +51,14 @@ class FileList extends React.PureComponent {
};
}
getChannelSignature(fileInfo) {
if (fileInfo.value) {
return fileInfo.value.publisherSignature.certificateId;
} else {
return fileInfo.metadata.publisherSignature.certificateId;
}
}
handleSortChanged(event) {
this.setState({
sortBy: event.target.value,
@ -67,12 +71,12 @@ class FileList extends React.PureComponent {
const content = [];
this._sortFunctions[sortBy](fileInfos).forEach(fileInfo => {
let uriParams = {
claimId: fileInfo.claim_id,
};
let uriParams = {};
if (fileInfo.channel_name) {
uriParams.channelName = fileInfo.channel_name;
uriParams.contentName = fileInfo.name;
uriParams.claimId = this.getChannelSignature(fileInfo);
} else {
uriParams.claimId = fileInfo.claim_id;
uriParams.name = fileInfo.name;

View file

@ -19,7 +19,7 @@ class FilePrice extends React.PureComponent {
}
render() {
const { costInfo, look = "indicator" } = this.props;
const { costInfo, look = "indicator", showFullPrice = false } = this.props;
const isEstimate = costInfo ? !costInfo.includesData : null;
@ -35,6 +35,7 @@ class FilePrice extends React.PureComponent {
amount={costInfo.cost}
isEstimate={isEstimate}
showFree={true}
showFullPrice={showFullPrice}
/>
);
}

View file

@ -71,7 +71,8 @@ class FileTile extends React.PureComponent {
? metadata.thumbnail
: null;
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
const isRewardContent =
claim && rewardedContentClaimIds.includes(claim.claim_id);
let onClick = () => navigate("/show", { uri });
@ -109,7 +110,7 @@ class FileTile extends React.PureComponent {
<div className="file-tile__content">
<div className="card__title-primary">
{!hidePrice ? <FilePrice uri={this.props.uri} /> : null}
{isRewardContent && <IconFeatured /> }
{isRewardContent && <IconFeatured />}
<div className="meta">{uri}</div>
<h3>
<TruncatedText lines={1}>{title}</TruncatedText>

View file

@ -14,21 +14,23 @@ class FormFieldPrice extends React.PureComponent {
};
}
dispatchChange() {
handleChange(newValues) {
const newState = Object.assign({}, this.state, newValues);
this.setState(newState);
this.props.onChange({
amount: this.state.amount,
currency: this.state.currency,
amount: newState.amount,
currency: newState.currency,
});
}
handleFeeAmountChange(event) {
this.state.amount = event.target.value ? Number(event.target.value) : null;
this.dispatchChange();
this.handleChange({
amount: event.target.value ? Number(event.target.value) : null,
});
}
handleFeeCurrencyChange(event) {
this.state.currency = event.target.value;
this.dispatchChange();
this.handleChange({ currency: event.target.value });
}
render() {

View file

@ -1,11 +1,14 @@
import React from "react";
import { formatCredits } from "utils";
import { formatCredits } from "util/formatCredits";
import { connect } from "react-redux";
import { selectIsBackDisabled, selectIsForwardDisabled } from "selectors/app";
import { selectBalance } from "selectors/wallet";
import { doNavigate, doHistoryBack } from "actions/app";
import { doNavigate, doHistoryBack, doHistoryForward } from "actions/app";
import Header from "./view";
const select = state => ({
isBackDisabled: selectIsBackDisabled(state),
isForwardDisabled: selectIsForwardDisabled(state),
balance: formatCredits(selectBalance(state), 1),
publish: __("Publish"),
});
@ -13,6 +16,7 @@ const select = state => ({
const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
back: () => dispatch(doHistoryBack()),
forward: () => dispatch(doHistoryForward()),
});
export default connect(select, perform)(Header);

View file

@ -3,12 +3,34 @@ import Link from "component/link";
import WunderBar from "component/wunderbar";
export const Header = props => {
const { balance, back, navigate, publish } = props;
const {
balance,
back,
forward,
isBackDisabled,
isForwardDisabled,
navigate,
publish,
} = props;
return (
<header id="header">
<div className="header__item">
<Link onClick={back} button="alt button--flat" icon="icon-arrow-left" title={__("Back")} />
<Link
onClick={back}
disabled={isBackDisabled}
button="alt button--flat"
icon="icon-arrow-left"
title={__("Back")}
/>
</div>
<div className="header__item">
<Link
onClick={forward}
disabled={isForwardDisabled}
button="alt button--flat"
icon="icon-arrow-right"
title={__("Forward")}
/>
</div>
<div className="header__item">
<Link

View file

@ -3,10 +3,11 @@ import { Icon } from "component/common.js";
const IconFeatured = props => {
return (
<span className="icon-featured" title={ __("Watch content with this icon to earn weekly rewards.")}>
<Icon icon="icon-rocket"
fixed
className="card__icon-featured-content" />
<span
className="icon-featured"
title={__("Watch content with this icon to earn weekly rewards.")}
>
<Icon icon="icon-rocket" fixed className="card__icon-featured-content" />
</span>
);
};

View file

@ -17,11 +17,9 @@ const Link = props => {
const className =
(props.className || "") +
(!props.className && !props.button ? "button-text" : "") + // Non-button links get the same look as text buttons
(props.button
? " button-block button-" + props.button + " button-set-item"
: "") +
(props.disabled ? " disabled" : "");
(!props.className && !button ? "button-text" : "") + // Non-button links get the same look as text buttons
(button ? " button-block button-" + button + " button-set-item" : "") +
(disabled ? " disabled" : "");
let content;
if (children) {

View file

@ -69,7 +69,8 @@ export class DropDownMenu extends React.PureComponent {
});
}
handleWindowClick(e) {
/*this will force "this" to always be the class, even when passed to an event listener*/
handleWindowClick = e => {
if (
this.state.menuOpen &&
(!this._menuDiv || !this._menuDiv.contains(e.target))
@ -78,7 +79,7 @@ export class DropDownMenu extends React.PureComponent {
menuOpen: false,
});
}
}
};
render() {
if (!this.state.menuOpen && this._isWindowClickBound) {

View file

@ -1,84 +0,0 @@
import React from "react";
import { Modal } from "component/modal";
import { CreditAmount } from "component/common";
import Link from "component/link";
import RewardLink from "component/rewardLink";
class ModalWelcome extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isFirstScreen: true,
};
}
render() {
const { closeModal, totalRewardValue, verifyAccount } = this.props;
const totalRewardRounded = Math.round(totalRewardValue / 10) * 10;
return (
<Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
{this.state.isFirstScreen &&
<section>
<h3 className="modal__header">{__("Welcome to LBRY")}</h3>
<p>
{__(
"Using LBRY is like dating a centaur. Totally normal up top, and"
)}
{" "}<em>{__("way different")}</em> {__("underneath.")}
</p>
<p>{__("Up top, LBRY is similar to popular media sites.")}</p>
<p>
{__(
"Below, LBRY is controlled by users -- you -- via blockchain and decentralization."
)}
</p>
<div className="modal__buttons">
<Link
button="primary"
onClick={() => {
this.setState({ isFirstScreen: false });
}}
label={__("Continue")}
/>
</div>
</section>}
{!this.state.isFirstScreen &&
<section>
<h3 className="modal__header">{__("Claim Your Credits")}</h3>
<p>
The LBRY network is controlled and powered by credits called{" "}
<em>LBC</em>, a blockchain asset.
</p>
<p>
{__("New patrons receive ")} {" "}
{totalRewardValue
? <CreditAmount amount={totalRewardRounded} />
: <span className="credit-amount">{__("credits")}</span>}
{" "} {__("in rewards for usage and influence of the network.")}
</p>
<p>
{__(
"You'll also earn weekly bonuses for checking out the greatest new stuff."
)}
</p>
<div className="modal__buttons">
<Link
button="primary"
onClick={verifyAccount}
label={__("You Had Me At Free LBC")}
/>
<Link
button="alt"
onClick={closeModal}
label={__("I Burn Money")}
/>
</div>
</section>}
</Modal>
);
}
}
export default ModalWelcome;

View file

@ -55,7 +55,7 @@ class ChannelSection extends React.PureComponent {
handleCreateChannelClick(event) {
if (this.state.newChannelName.length < 5) {
this.refs.newChannelName.showError(
__("LBRY channel names must be at least 4 characters in length.")
__("LBRY channel names must be at least 5 characters in length.")
);
return;
}

View file

@ -5,7 +5,7 @@ import FormField from "component/formField";
import { FormRow } from "component/form.js";
import Link from "component/link";
import FormFieldPrice from "component/formFieldPrice";
import Modal from "component/modal";
import Modal from "modal/modal";
import { BusyMessage } from "component/common";
import ChannelSection from "./internal/channelSection";
@ -377,46 +377,6 @@ class PublishForm extends React.PureComponent {
});
}
handleCreateChannelClick(event) {
if (this.state.newChannelName.length < 5) {
this.refs.newChannelName.showError(
__("LBRY channel names must be at least 4 characters in length.")
);
return;
}
this.setState({
creatingChannel: true,
});
const newChannelName = this.state.newChannelName;
lbry
.channel_new({
channel_name: newChannelName,
amount: parseFloat(this.state.newChannelBid),
})
.then(
() => {
setTimeout(() => {
this.setState({
creatingChannel: false,
});
this._updateChannelList(newChannelName);
}, 10000);
},
error => {
// TODO: better error handling
this.refs.newChannelName.showError(
__("Unable to create channel due to an internal error.")
);
this.setState({
creatingChannel: false,
});
}
);
}
getLicense() {
switch (this.state.licenseType) {
case "copyright":
@ -564,6 +524,7 @@ class PublishForm extends React.PureComponent {
: <div>
<div className="card__content">
<FormRow
ref="meta_title"
label={__("Title")}
type="text"
name="title"
@ -825,6 +786,7 @@ class PublishForm extends React.PureComponent {
ref="bid"
type="number"
step="0.01"
min="0"
label={__("Deposit")}
postfix="LBC"
onChange={event => {
@ -833,6 +795,7 @@ class PublishForm extends React.PureComponent {
value={this.state.bid}
placeholder={this.claim() ? this.topClaimValue() + 10 : 100}
helper={lbcInputHelp}
min="0"
/>
</div>
: ""}
@ -844,6 +807,7 @@ class PublishForm extends React.PureComponent {
</div>
<div className="card__content">
<FormRow
ref="tosAgree"
label={
<span>
{__("I agree to the")}

View file

@ -1,5 +1,5 @@
import React from "react";
import Modal from "component/modal";
import Modal from "modal/modal";
import Link from "component/link";
const RewardLink = props => {

View file

@ -14,6 +14,7 @@ import FileListPublished from "page/fileListPublished";
import ChannelPage from "page/channel";
import SearchPage from "page/search";
import AuthPage from "page/auth";
import BackupPage from "page/backup";
const route = (page, routesMap) => {
const component = routesMap[page];
@ -26,6 +27,7 @@ const Router = props => {
return route(currentPage, {
auth: <AuthPage params={params} />,
backup: <BackupPage params={params} />,
channel: <ChannelPage params={params} />,
developer: <DeveloperPage params={params} />,
discover: <DiscoverPage params={params} />,

View file

@ -1,9 +1,9 @@
import React from "react";
import lbry from "../../lbry.js";
import lbry from "lbry.js";
import LoadScreen from "../load_screen.js";
import ModalIncompatibleDaemon from "../modalIncompatibleDaemon";
import ModalUpgrade from "component/modalUpgrade";
import ModalDownloading from "component/modalDownloading";
import ModalIncompatibleDaemon from "modal/modalIncompatibleDaemon";
import ModalUpgrade from "modal/modalUpgrade";
import ModalDownloading from "modal/modalDownloading";
export class SplashScreen extends React.PureComponent {
static propTypes = {

View file

@ -34,7 +34,7 @@ class UserEmailNew extends React.PureComponent {
<FormRow
type="text"
label="Email"
placeholder="scrwvwls@lbry.io"
placeholder="youremail@example.org"
name="email"
value={this.state.email}
errorMessage={errorMessage}

View file

@ -21,7 +21,7 @@ const select = (state, props) => {
};
const perform = dispatch => ({
navigate: (uri) => dispatch(doNavigate(uri)),
navigate: uri => dispatch(doNavigate(uri)),
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
});

View file

@ -1,7 +1,7 @@
import React from "react";
import { CreditAmount } from "component/common";
import Link from "component/link";
import CardVerify from "component/cardVerify";
import lbryio from "lbryio.js";
class UserVerify extends React.PureComponent {
constructor(props) {
@ -29,20 +29,34 @@ class UserVerify extends React.PureComponent {
<p>
{__(
"To ensure you are a real person, we require a valid credit or debit card."
) + " " + __("There is no charge at all, now or in the future.") + " " }
<Link href="https://lbry.io/faq/identity-requirements" label={__("Read More")} />
) +
" " +
__("There is no charge at all, now or in the future.") +
" "}
<Link
href="https://lbry.io/faq/identity-requirements"
label={__("Read More")}
/>
</p>
{errorMessage && <p className="form-field__error">{errorMessage}</p>}
<p><CardVerify
label={__("Link Card and Finish")}
disabled={isPending}
token={this.onToken.bind(this)}
stripeKey="pk_live_e8M4dRNnCCbmpZzduEUZBgJO"
/></p>
<p>
{__("You can continue without this step, but you will not be eligible to earn rewards.")}
<CardVerify
label={__("Link Card and Finish")}
disabled={isPending}
token={this.onToken.bind(this)}
stripeKey={lbryio.getStripeToken()}
/>
</p>
<Link onClick={() => navigate("/discover")} button="alt" label={__("Skip Rewards")} />
<p>
{__(
"You can continue without this step, but you will not be eligible to earn rewards."
)}
</p>
<Link
onClick={() => navigate("/discover")}
button="alt"
label={__("Skip Rewards")}
/>
</div>
);
}

View file

@ -1,8 +1,8 @@
import React from "react";
import { connect } from "react-redux";
import { doCloseModal } from "actions/app";
import { doNavigate } from "actions/app";
import { selectCurrentModal } from "selectors/app";
import { doNavigate, doChangeVolume } from "actions/app";
import { selectCurrentModal, selectVolume } from "selectors/app";
import { doPurchaseUri, doLoadVideo } from "actions/content";
import {
makeSelectMetadataForUri,
@ -34,6 +34,7 @@ const makeSelect = () => {
isLoading: selectIsLoading(state, props),
isDownloading: selectIsDownloading(state, props),
contentType: selectContentType(state, props),
volume: selectVolume(state, props),
});
return select;
@ -43,6 +44,7 @@ const perform = dispatch => ({
loadVideo: uri => dispatch(doLoadVideo(uri)),
purchaseUri: uri => dispatch(doPurchaseUri(uri, "affirmPurchaseAndPlay")),
closeModal: () => dispatch(doCloseModal()),
changeVolume: volume => dispatch(doChangeVolume(volume)),
});
export default connect(makeSelect, perform)(Video);

View file

@ -1,7 +1,7 @@
import React from "react";
import FilePrice from "component/filePrice";
import Link from "component/link";
import Modal from "component/modal";
import Modal from "modal/modal";
class VideoPlayButton extends React.PureComponent {
componentDidMount() {
@ -83,7 +83,7 @@ class VideoPlayButton extends React.PureComponent {
>
{__("This will purchase")} <strong>{title}</strong> {__("for")}{" "}
<strong>
<FilePrice uri={uri} look="plain" />
<FilePrice uri={uri} showFullPrice={true} look="plain" />
</strong>{" "}
{__("credits")}.
</Modal>

View file

@ -3,7 +3,6 @@ import React from "react";
import { Thumbnail } from "component/common";
import player from "render-media";
import fs from "fs";
import { setSession, getSession } from "utils";
import LoadingScreen from "./loading-screen";
class VideoPlayer extends React.PureComponent {
@ -23,7 +22,13 @@ class VideoPlayer extends React.PureComponent {
componentDidMount() {
const container = this.refs.media;
const { contentType, downloadPath, mediaType } = this.props;
const {
contentType,
downloadPath,
mediaType,
changeVolume,
volume,
} = this.props;
const loadedMetadata = e => {
this.setState({ hasMetadata: true, startedPlaying: true });
this.refs.media.children[0].play();
@ -69,9 +74,9 @@ class VideoPlayer extends React.PureComponent {
win32FullScreenChange.bind(this)
);
mediaElement.addEventListener("volumechange", () => {
setSession("prefs_volume", mediaElement.volume);
changeVolume(mediaElement.volume);
});
mediaElement.volume = this.getPreferredVolume();
mediaElement.volume = volume;
}
}
@ -116,11 +121,6 @@ class VideoPlayer extends React.PureComponent {
}
}
getPreferredVolume() {
const volumePreference = parseFloat(getSession("prefs_volume"));
return isNaN(volumePreference) ? 1 : volumePreference;
}
componentDidUpdate() {
const { contentType, downloadCompleted } = this.props;
const { startedPlaying } = this.state;

View file

@ -16,7 +16,11 @@ class Video extends React.PureComponent {
componentWillReceiveProps(nextProps) {
// reset playing state upon change path action
if (!this.isMediaSame(nextProps) && this.props.fileInfo && this.state.isPlaying) {
if (
!this.isMediaSame(nextProps) &&
this.props.fileInfo &&
this.state.isPlaying
) {
this.state.isPlaying = false;
}
}
@ -62,6 +66,8 @@ class Video extends React.PureComponent {
isDownloading,
fileInfo,
contentType,
changeVolume,
volume,
} = this.props;
const { isPlaying = false } = this.state;
@ -115,6 +121,8 @@ class Video extends React.PureComponent {
mediaType={mediaType}
contentType={contentType}
downloadCompleted={fileInfo.completed}
changeVolume={changeVolume}
volume={volume}
/>)}
{!isPlaying &&
<div

View file

@ -1,5 +1,6 @@
import React from "react";
import Link from "component/link";
import Modal from "modal/modal";
import { FormRow } from "component/form";
const WalletSend = props => {
@ -16,6 +17,7 @@ const WalletSend = props => {
label={__("Amount")}
postfix={__("LBC")}
step="0.01"
min="0"
type="number"
placeholder="1.23"
size="10"

View file

@ -1,7 +1,7 @@
export const CHANGE_PATH = "CHANGE_PATH";
export const OPEN_MODAL = "OPEN_MODAL";
export const CLOSE_MODAL = "CLOSE_MODAL";
export const HISTORY_BACK = "HISTORY_BACK";
export const HISTORY_NAVIGATE = "HISTORY_NAVIGATE";
export const SHOW_SNACKBAR = "SHOW_SNACKBAR";
export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK";
export const WINDOW_FOCUSED = "WINDOW_FOCUSED";
@ -9,6 +9,7 @@ export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
export const DAEMON_READY = "DAEMON_READY";
export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH";
export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH";
export const VOLUME_CHANGED = "VOLUME_CHANGED";
// Upgrades
export const UPGRADE_CANCELLED = "UPGRADE_CANCELLED";
@ -45,6 +46,10 @@ export const RESOLVE_URI_COMPLETED = "RESOLVE_URI_COMPLETED";
export const RESOLVE_URI_CANCELED = "RESOLVE_URI_CANCELED";
export const FETCH_CHANNEL_CLAIMS_STARTED = "FETCH_CHANNEL_CLAIMS_STARTED";
export const FETCH_CHANNEL_CLAIMS_COMPLETED = "FETCH_CHANNEL_CLAIMS_COMPLETED";
export const FETCH_CHANNEL_CLAIM_COUNT_STARTED =
"FETCH_CHANNEL_CLAIM_COUNT_STARTED";
export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED =
"FETCH_CHANNEL_CLAIM_COUNT_COMPLETED";
export const FETCH_CLAIM_LIST_MINE_STARTED = "FETCH_CLAIM_LIST_MINE_STARTED";
export const FETCH_CLAIM_LIST_MINE_COMPLETED =
"FETCH_CLAIM_LIST_MINE_COMPLETED";
@ -118,3 +123,7 @@ export const FETCH_REWARD_CONTENT_COMPLETED = "FETCH_REWARD_CONTENT_COMPLETED";
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
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED";
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED";

View file

@ -0,0 +1,187 @@
const LANGUAGES = {
aa: ["Afar", "Afar"],
ab: ["Abkhazian", "Аҧсуа"],
af: ["Afrikaans", "Afrikaans"],
ak: ["Akan", "Akana"],
am: ["Amharic", "አማርኛ"],
an: ["Aragonese", "Aragonés"],
ar: ["Arabic", "العربية"],
as: ["Assamese", "অসমীয়া"],
av: ["Avar", "Авар"],
ay: ["Aymara", "Aymar"],
az: ["Azerbaijani", "Azərbaycanca / آذربايجان"],
ba: ["Bashkir", "Башҡорт"],
be: ["Belarusian", "Беларуская"],
bg: ["Bulgarian", "Български"],
bh: ["Bihari", "भोजपुरी"],
bi: ["Bislama", "Bislama"],
bm: ["Bambara", "Bamanankan"],
bn: ["Bengali", "বাংলা"],
bo: ["Tibetan", "བོད་ཡིག / Bod skad"],
br: ["Breton", "Brezhoneg"],
bs: ["Bosnian", "Bosanski"],
ca: ["Catalan", "Català"],
ce: ["Chechen", "Нохчийн"],
ch: ["Chamorro", "Chamoru"],
co: ["Corsican", "Corsu"],
cr: ["Cree", "Nehiyaw"],
cs: ["Czech", "Česky"],
cu: ["Old Church Slavonic / Old Bulgarian", "словѣньскъ / slověnĭskŭ"],
cv: ["Chuvash", "Чăваш"],
cy: ["Welsh", "Cymraeg"],
da: ["Danish", "Dansk"],
de: ["German", "Deutsch"],
dv: ["Divehi", "ދިވެހިބަސް"],
dz: ["Dzongkha", "ཇོང་ཁ"],
ee: ["Ewe", "Ɛʋɛ"],
el: ["Greek", "Ελληνικά"],
en: ["English", "English"],
eo: ["Esperanto", "Esperanto"],
es: ["Spanish", "Español"],
et: ["Estonian", "Eesti"],
eu: ["Basque", "Euskara"],
fa: ["Persian", "فارسی"],
ff: ["Peul", "Fulfulde"],
fi: ["Finnish", "Suomi"],
fj: ["Fijian", "Na Vosa Vakaviti"],
fo: ["Faroese", "Føroyskt"],
fr: ["French", "Français"],
fy: ["West Frisian", "Frysk"],
ga: ["Irish", "Gaeilge"],
gd: ["Scottish Gaelic", "Gàidhlig"],
gl: ["Galician", "Galego"],
gn: ["Guarani", "Avañe'ẽ"],
gu: ["Gujarati", "ગુજરાતી"],
gv: ["Manx", "Gaelg"],
ha: ["Hausa", "هَوُسَ"],
he: ["Hebrew", "עברית"],
hi: ["Hindi", "हिन्दी"],
ho: ["Hiri Motu", "Hiri Motu"],
hr: ["Croatian", "Hrvatski"],
ht: ["Haitian", "Krèyol ayisyen"],
hu: ["Hungarian", "Magyar"],
hy: ["Armenian", "Հայերեն"],
hz: ["Herero", "Otsiherero"],
ia: ["Interlingua", "Interlingua"],
id: ["Indonesian", "Bahasa Indonesia"],
ie: ["Interlingue", "Interlingue"],
ig: ["Igbo", "Igbo"],
ii: ["Sichuan Yi", "ꆇꉙ / 四川彝语"],
ik: ["Inupiak", "Iñupiak"],
io: ["Ido", "Ido"],
is: ["Icelandic", "Íslenska"],
it: ["Italian", "Italiano"],
iu: ["Inuktitut", "ᐃᓄᒃᑎᑐᑦ"],
ja: ["Japanese", "日本語"],
jv: ["Javanese", "Basa Jawa"],
ka: ["Georgian", "ქართული"],
kg: ["Kongo", "KiKongo"],
ki: ["Kikuyu", "Gĩkũyũ"],
kj: ["Kuanyama", "Kuanyama"],
kk: ["Kazakh", "Қазақша"],
kl: ["Greenlandic", "Kalaallisut"],
km: ["Cambodian", "ភាសាខ្មែរ"],
kn: ["Kannada", "ಕನ್ನಡ"],
ko: ["Korean", "한국어"],
kr: ["Kanuri", "Kanuri"],
ks: ["Kashmiri", "कश्मीरी / كشميري"],
ku: ["Kurdish", "Kurdî / كوردی"],
kv: ["Komi", "Коми"],
kw: ["Cornish", "Kernewek"],
ky: ["Kirghiz", "Kırgızca / Кыргызча"],
la: ["Latin", "Latina"],
lb: ["Luxembourgish", "Lëtzebuergesch"],
lg: ["Ganda", "Luganda"],
li: ["Limburgian", "Limburgs"],
ln: ["Lingala", "Lingála"],
lo: ["Laotian", "ລາວ / Pha xa lao"],
lt: ["Lithuanian", "Lietuvių"],
lv: ["Latvian", "Latviešu"],
mg: ["Malagasy", "Malagasy"],
mh: ["Marshallese", "Kajin Majel / Ebon"],
mi: ["Maori", "Māori"],
mk: ["Macedonian", "Македонски"],
ml: ["Malayalam", "മലയാളം"],
mn: ["Mongolian", "Монгол"],
mo: ["Moldovan", "Moldovenească"],
mr: ["Marathi", "मराठी"],
ms: ["Malay", "Bahasa Melayu"],
mt: ["Maltese", "bil-Malti"],
my: ["Burmese", "Myanmasa"],
na: ["Nauruan", "Dorerin Naoero"],
nd: ["North Ndebele", "Sindebele"],
ne: ["Nepali", "नेपाली"],
ng: ["Ndonga", "Oshiwambo"],
nl: ["Dutch", "Nederlands"],
nn: ["Norwegian Nynorsk", "Norsk (nynorsk)"],
no: ["Norwegian", "Norsk (bokmål / riksmål)"],
nr: ["South Ndebele", "isiNdebele"],
nv: ["Navajo", "Diné bizaad"],
ny: ["Chichewa", "Chi-Chewa"],
oc: ["Occitan", "Occitan"],
oj: ["Ojibwa", "ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin"],
om: ["Oromo", "Oromoo"],
or: ["Oriya", "ଓଡ଼ିଆ"],
os: ["Ossetian / Ossetic", "Иронау"],
pa: ["Panjabi / Punjabi", "ਪੰਜਾਬੀ / पंजाबी / پنجابي"],
pi: ["Pali", "Pāli / पाऴि"],
pl: ["Polish", "Polski"],
ps: ["Pashto", "پښتو"],
pt: ["Portuguese", "Português"],
qu: ["Quechua", "Runa Simi"],
rm: ["Raeto Romance", "Rumantsch"],
rn: ["Kirundi", "Kirundi"],
ro: ["Romanian", "Română"],
ru: ["Russian", "Русский"],
rw: ["Rwandi", "Kinyarwandi"],
sa: ["Sanskrit", "संस्कृतम्"],
sc: ["Sardinian", "Sardu"],
sd: ["Sindhi", "सिनधि"],
se: ["Northern Sami", "Sámegiella"],
sg: ["Sango", "Sängö"],
sh: ["Serbo-Croatian", "Srpskohrvatski / Српскохрватски"],
si: ["Sinhalese", "සිංහල"],
sk: ["Slovak", "Slovenčina"],
sl: ["Slovenian", "Slovenščina"],
sm: ["Samoan", "Gagana Samoa"],
sn: ["Shona", "chiShona"],
so: ["Somalia", "Soomaaliga"],
sq: ["Albanian", "Shqip"],
sr: ["Serbian", "Српски"],
ss: ["Swati", "SiSwati"],
st: ["Southern Sotho", "Sesotho"],
su: ["Sundanese", "Basa Sunda"],
sv: ["Swedish", "Svenska"],
sw: ["Swahili", "Kiswahili"],
ta: ["Tamil", "தமிழ்"],
te: ["Telugu", "తెలుగు"],
tg: ["Tajik", "Тоҷикӣ"],
th: ["Thai", "ไทย / Phasa Thai"],
ti: ["Tigrinya", "ትግርኛ"],
tk: ["Turkmen", "Туркмен / تركمن"],
tl: ["Tagalog / Filipino", "Tagalog"],
tn: ["Tswana", "Setswana"],
to: ["Tonga", "Lea Faka-Tonga"],
tr: ["Turkish", "Türkçe"],
ts: ["Tsonga", "Xitsonga"],
tt: ["Tatar", "Tatarça"],
tw: ["Twi", "Twi"],
ty: ["Tahitian", "Reo Mā`ohi"],
ug: ["Uyghur", "Uyƣurqə / ئۇيغۇرچە"],
uk: ["Ukrainian", "Українська"],
ur: ["Urdu", "اردو"],
uz: ["Uzbek", "Ўзбек"],
ve: ["Venda", "Tshivenḓa"],
vi: ["Vietnamese", "Tiếng Việt"],
vo: ["Volapük", "Volapük"],
wa: ["Walloon", "Walon"],
wo: ["Wolof", "Wollof"],
xh: ["Xhosa", "isiXhosa"],
yi: ["Yiddish", "ייִדיש"],
yo: ["Yoruba", "Yorùbá"],
za: ["Zhuang", "Cuengh / Tôô / 壮语"],
zh: ["Chinese", "中文"],
zu: ["Zulu", "isiZulu"],
};
export default LANGUAGES;

View file

@ -9,3 +9,4 @@ export const FIRST_REWARD = "first_reward";
export const AUTHENTICATION_FAILURE = "auth_failure";
export const TRANSACTION_FAILED = "transaction_failed";
export const INSUFFICIENT_BALANCE = "insufficient_balance";
export const CREDIT_INTRO = "credit_intro";

View file

@ -0,0 +1,5 @@
export const CREDIT_INTRO_ACKNOWLEDGED = "credit_intro_acknowledged";
export const FIRST_RUN_ACKNOWLEDGED = "welcome_acknowledged";
export const LANGUAGE = "language";
export const SHOW_NSFW = "showNsfw";
export const SHOW_UNAVAILABLE = "showUnavailable";

View file

@ -27,7 +27,7 @@ jsonrpc.call = function(
xhr.addEventListener("load", function() {
var response = JSON.parse(xhr.responseText);
let error = response.error || response.result && response.result.error;
let error = response.error || (response.result && response.result.error);
if (error) {
if (errorCallback) {
errorCallback(error);

View file

@ -1,15 +1,35 @@
import lbryio from "./lbryio.js";
import lighthouse from "./lighthouse.js";
import jsonrpc from "./jsonrpc.js";
import lbryuri from "./lbryuri.js";
import { getLocal, getSession, setSession, setLocal } from "./utils.js";
/**
* The 4 get/set functions below used to be in a utils.js library when used more widely.
* They've been reduced to just this file and probably ought to be eliminated entirely.
*/
function getLocal(key, fallback = undefined) {
const itemRaw = localStorage.getItem(key);
return itemRaw === null ? fallback : JSON.parse(itemRaw);
}
function setLocal(key, value) {
localStorage.setItem(key, JSON.stringify(value));
}
function getSession(key, fallback = undefined) {
const itemRaw = sessionStorage.getItem(key);
return itemRaw === null ? fallback : JSON.parse(itemRaw);
}
function setSession(key, value) {
sessionStorage.setItem(key, JSON.stringify(value));
}
const { remote, ipcRenderer } = require("electron");
const menu = remote.require("./menu/main-menu");
let lbry = {
isConnected: false,
daemonConnectionString: "http://localhost:5279/lbryapi",
daemonConnectionString: "http://localhost:5279",
pendingPublishTimeout: 20 * 60 * 1000,
defaultClientSettings: {
showNsfw: false,

View file

@ -148,7 +148,6 @@ lbryio.authenticate = function() {
has_verified_email: true,
is_identity_verified: true,
is_reward_approved: false,
is_reward_eligible: false,
});
});
}
@ -177,32 +176,20 @@ lbryio.authenticate = function() {
return;
}
let app_id;
return lbry
.status()
.then(status => {
// first try swapping
app_id = status.installation_id;
return lbryio.call(
"user",
"token_swap",
{ auth_token: "", app_id: app_id },
"new",
{
auth_token: "",
language: "en",
app_id: status.installation_id,
},
"post"
);
})
.catch(err => {
if (err.xhr.status == 403) {
// cannot swap. either app_id doesn't exist, or app_id already swapped. pretend its the former and create a new user. if we get another error, then its the latter
return lbryio.call(
"user",
"new",
{ auth_token: "", language: "en", app_id: app_id },
"post"
);
}
throw err;
})
.then(response => {
if (!response.auth_token) {
throw new Error(__("auth_token is missing from response"));
@ -218,4 +205,10 @@ lbryio.authenticate = function() {
return lbryio._authenticationPromise;
};
lbryio.getStripeToken = () => {
return CONNECTION_STRING.startsWith("http://localhost:")
? "pk_test_NoL1JWL7i1ipfhVId5KfDZgo"
: "pk_live_e8M4dRNnCCbmpZzduEUZBgJO";
};
export default lbryio;

View file

@ -7,6 +7,7 @@ import { Provider } from "react-redux";
import store from "store.js";
import SplashScreen from "component/splash";
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
import { doDownloadLanguages } from "actions/settings";
import { toQueryString } from "util/query_params";
import * as types from "constants/action_types";
@ -96,6 +97,8 @@ const updateProgress = () => {
const initialState = app.store.getState();
var init = function() {
app.store.dispatch(doDownloadLanguages());
function onDaemonReady() {
window.sessionStorage.setItem("loaded", "y"); //once we've made it here once per session, we don't need to show splash again
app.store.dispatch(doDaemonReady());

View file

@ -1,7 +1,7 @@
import React from "react";
import ReactModal from "react-modal";
import Link from "component/link";
import app from "../app.js";
import Link from "component/link/index";
import app from "app.js";
export class Modal extends React.PureComponent {
static propTypes = {

View file

@ -1,5 +1,5 @@
import React from "react";
import { Modal } from "component/modal";
import { Modal } from "modal/modal";
class ModalAuthFailure extends React.PureComponent {
render() {
@ -12,11 +12,17 @@ class ModalAuthFailure extends React.PureComponent {
type="confirm"
confirmButtonLabel={__("Reload")}
abortButtonLabel={__("Continue")}
onConfirmed={() => { window.location.reload() }}
onConfirmed={() => {
window.location.reload();
}}
onAborted={close}
>
<h3>{__("Authentication Failure")}</h3>
<p>{__("If reloading does not fix this, or you see this at every start up, please email help@lbry.io.")}</p>
<p>
{__(
"If reloading does not fix this, or you see this at every start up, please email help@lbry.io."
)}
</p>
</Modal>
);
}

View file

@ -9,7 +9,8 @@ import {
makeSelectRewardByType,
selectTotalRewardValue,
} from "selectors/rewards";
import ModalWelcome from "./view";
import * as settings from "constants/settings";
import ModalCreditIntro from "./view";
const select = (state, props) => {
const selectHasClaimed = makeSelectHasClaimedReward(),
@ -24,7 +25,7 @@ const select = (state, props) => {
const perform = dispatch => () => {
const closeModal = () => {
dispatch(doSetClientSetting("welcome_acknowledged", true));
dispatch(doSetClientSetting(settings.CREDIT_INTRO_ACKNOWLEDGED, true));
dispatch(doCloseModal());
};
@ -37,4 +38,4 @@ const perform = dispatch => () => {
};
};
export default connect(select, perform)(ModalWelcome);
export default connect(select, perform)(ModalCreditIntro);

View file

@ -0,0 +1,44 @@
import React from "react";
import { Modal } from "modal/modal";
import { CreditAmount } from "component/common";
import Link from "component/link/index";
const ModalCreditIntro = props => {
const { closeModal, totalRewardValue, verifyAccount } = props;
const totalRewardRounded = Math.round(totalRewardValue / 10) * 10;
return (
<Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
<section>
<h3 className="modal__header">{__("Claim Your Credits")}</h3>
<p>
The LBRY network is controlled and powered by credits called{" "}
<em>LBC</em>, a blockchain asset.
</p>
<p>
{__("New patrons receive ")} {" "}
{totalRewardValue
? <CreditAmount amount={totalRewardRounded} />
: <span className="credit-amount">{__("credits")}</span>}
{" "} {__("in rewards for usage and influence of the network.")}
</p>
<p>
{__(
"You'll also earn weekly bonuses for checking out the greatest new stuff."
)}
</p>
<div className="modal__buttons">
<Link
button="primary"
onClick={verifyAccount}
label={__("You Had Me At Free LBC")}
/>
<Link button="alt" onClick={closeModal} label={__("I Burn Money")} />
</div>
</section>
</Modal>
);
};
export default ModalCreditIntro;

View file

@ -1,7 +1,7 @@
import React from "react";
import { Modal } from "component/modal";
import { Modal } from "modal/modal";
import { Line } from "rc-progress";
import Link from "component/link";
import Link from "component/link/index";
class ModalDownloading extends React.PureComponent {
render() {

View file

@ -1,6 +1,6 @@
import React from "react";
import lbry from "lbry";
import { ExpandableModal } from "component/modal";
import { ExpandableModal } from "modal/modal";
class ModalError extends React.PureComponent {
render() {

View file

@ -1,5 +1,5 @@
import React from "react";
import { Modal } from "component/modal";
import { Modal } from "modal/modal";
import { CreditAmount } from "component/common";
class ModalFirstReward extends React.PureComponent {

View file

@ -1,6 +1,6 @@
import React from "react";
import { Modal } from "component/modal";
import Link from "component/link";
import { Modal } from "modal/modal";
import Link from "component/link/index";
class ModalIncompatibleDaemon extends React.PureComponent {
render() {

Some files were not shown because too many files have changed in this diff Show more