Merge remote-tracking branch 'upstream/master' into tb2
|
@ -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}"
|
||||
|
||||
|
|
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -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
|
@ -10,8 +10,10 @@
|
|||
|
||||
|
||||
/app/dist
|
||||
/app/locales
|
||||
/app/node_modules
|
||||
/build/venv
|
||||
/build/daemon.ver
|
||||
/lbry-app-venv
|
||||
/lbry-app
|
||||
/lbry-venv
|
||||
|
|
24
CHANGELOG.md
|
@ -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
|
||||
*
|
||||
|
|
12
README.md
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
https://github.com/lbryio/lbry/releases/download/v0.14.2/lbrynet-daemon-v0.14.2-OSNAME.zip
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
@ -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>
|
||||
|
|
BIN
ui/dist/js/mediaelement/background.png
vendored
Before Width: | Height: | Size: 166 B |
BIN
ui/dist/js/mediaelement/bigplay.fw.png
vendored
Before Width: | Height: | Size: 78 KiB |
BIN
ui/dist/js/mediaelement/bigplay.png
vendored
Before Width: | Height: | Size: 2.9 KiB |
14
ui/dist/js/mediaelement/bigplay.svg
vendored
|
@ -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 |
BIN
ui/dist/js/mediaelement/controls-ted.png
vendored
Before Width: | Height: | Size: 1.5 KiB |
BIN
ui/dist/js/mediaelement/controls-wmp-bg.png
vendored
Before Width: | Height: | Size: 1.9 KiB |
BIN
ui/dist/js/mediaelement/controls-wmp.png
vendored
Before Width: | Height: | Size: 5.4 KiB |
BIN
ui/dist/js/mediaelement/controls.fw.png
vendored
Before Width: | Height: | Size: 79 KiB |
BIN
ui/dist/js/mediaelement/controls.png
vendored
Before Width: | Height: | Size: 1.8 KiB |
1
ui/dist/js/mediaelement/controls.svg
vendored
Before Width: | Height: | Size: 10 KiB |
BIN
ui/dist/js/mediaelement/flashmediaelement-cdn.swf
vendored
BIN
ui/dist/js/mediaelement/flashmediaelement-debug.swf
vendored
BIN
ui/dist/js/mediaelement/flashmediaelement.swf
vendored
9597
ui/dist/js/mediaelement/jquery.js
vendored
BIN
ui/dist/js/mediaelement/jumpforward.png
vendored
Before Width: | Height: | Size: 1.5 KiB |
BIN
ui/dist/js/mediaelement/loading.gif
vendored
Before Width: | Height: | Size: 6.1 KiB |
5816
ui/dist/js/mediaelement/mediaelement-and-player.js
vendored
2063
ui/dist/js/mediaelement/mediaelement.js
vendored
15
ui/dist/js/mediaelement/mediaelement.min.js
vendored
1000
ui/dist/js/mediaelement/mediaelementplayer.css
vendored
3736
ui/dist/js/mediaelement/mediaelementplayer.js
vendored
289
ui/dist/js/mediaelement/mejs-skins.css
vendored
|
@ -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 */
|
||||
|
||||
|
||||
|
BIN
ui/dist/js/mediaelement/silverlightmediaelement.xap
vendored
BIN
ui/dist/js/mediaelement/skipback.png
vendored
Before Width: | Height: | Size: 4.1 KiB |
3
ui/dist/quit.html
vendored
|
@ -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" />
|
||||
|
|
4
ui/dist/upgrade.html
vendored
|
@ -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">
|
||||
|
|
4
ui/dist/warning.html
vendored
|
@ -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">
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -111,7 +111,7 @@ export function doSendDraftTransaction() {
|
|||
};
|
||||
|
||||
lbry
|
||||
.send_amount_to_address({
|
||||
.wallet_send({
|
||||
amount: draftTx.amount,
|
||||
address: draftTx.address,
|
||||
})
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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")}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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} />,
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)),
|
||||
});
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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";
|
||||
|
|
187
ui/js/constants/languages.js
Normal 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;
|
|
@ -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";
|
||||
|
|
5
ui/js/constants/settings.js
Normal 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";
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 = {
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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);
|
44
ui/js/modal/modalCreditIntro/view.jsx
Normal 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;
|
|
@ -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() {
|
|
@ -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() {
|
|
@ -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 {
|
|
@ -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() {
|