diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 1143fe2b5..c8cf85307 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.12.2rc3 +current_version = 0.12.2rc5 commit = True tag = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)((?P[a-z]+)(?P\d+))? @@ -18,5 +18,9 @@ values = [bumpversion:file:app/package.json] +[bumpversion:file:app/package-lock.json] + [bumpversion:file:ui/package.json] +[bumpversion:file:ui/package-lock.json] + diff --git a/.gitignore b/.gitignore index 022473023..cee0d61d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,13 @@ -node_modules -LBRY-darwin-x64 -dist +/node_modules +/LBRY-darwin-x64 +/dist + +/ui/dist/css/* +/ui/dist/js/* +!/ui/dist/js/mediaelement +/ui/node_modules +/ui/.sass-cache + /app/dist /app/node_modules diff --git a/.travis.yml b/.travis.yml index 36a29909e..3308117f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,7 @@ -matrix: - include: - - os: osx - # Use generic language for osx - # python 2.7 is broken on osx on travis, so follow we have to specify the installation ourselves - # https://github.com/travis-ci/travis-ci/issues/2312#issuecomment-195620855 - language: generic - osx_image: xcode7.3 - +os: linux +dist: xenial install: - - wget https://www.python.org/ftp/python/2.7.13/python-2.7.13-macosx10.6.pkg - - sudo installer -pkg python-2.7.13-macosx10.6.pkg -target / - - pip install -U pip - - pip install pyinstaller - - wget https://nodejs.org/dist/v6.9.4/node-v6.9.4.pkg - - sudo installer -pkg node-v6.9.4.pkg -target / - - sudo npm install electron-packager -g - - ./build.sh - - electron-packager --electron-version=1.4.14 --overwrite electron LBRY + - rvm install 2.3.1 + - rvm use 2.3.1 && gem install danger --version '~> 4.0' && danger +# - FULL_BUILD=true ./build.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index f98234a7c..beef797e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,15 +9,19 @@ Web UI version numbers should always match the corresponding version of LBRY App ## [Unreleased] ### Added * State is persisted through app close and re-open, resulting in faster opens + * Support webm, ogg, m4v, and a few others + * Translations added to build process ### Changed * Upgraded to lbry daemon 0.13, including updating API signatures * Channels resolve much faster * Resolve is no longer cancelled on navigate + * Updated API and authentication used by rewards process ### Fixed * Fix help menu force reloading whole app * Show page updates correctly when navigating from show page to another show page + * URI handling navigates to correct page if app is closed ### Deprecated * diff --git a/ui/Dangerfile b/Dangerfile similarity index 100% rename from ui/Dangerfile rename to Dangerfile diff --git a/app/package-lock.json b/app/package-lock.json index 9ac1f3774..ece7eeafe 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "LBRY", - "version": "0.12.0", + "version": "0.12.2rc5", "lockfileVersion": 1, "dependencies": { "commander": { @@ -155,7 +155,8 @@ }, "dezalgo": { "version": "1.0.3", - "bundled": true + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=" }, "editor": { "version": "1.0.0", @@ -525,7 +526,8 @@ }, "normalize-git-url": { "version": "3.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/normalize-git-url/-/normalize-git-url-3.0.2.tgz", + "integrity": "sha1-jl8Uvgva7bc+ByADEKpBbCc1D8Q=" }, "normalize-package-data": { "version": "2.3.8", @@ -549,7 +551,8 @@ }, "npm-install-checks": { "version": "3.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-3.0.0.tgz", + "integrity": "sha1-1K7N/VGlPjcjt7L5Oy7ijjB7wNc=" }, "npm-package-arg": { "version": "4.2.1", @@ -717,7 +720,8 @@ }, "core-util-is": { "version": "1.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "isarray": { "version": "1.0.0", @@ -743,7 +747,8 @@ }, "realize-package-specifier": { "version": "3.0.3", - "bundled": true + "resolved": "https://registry.npmjs.org/realize-package-specifier/-/realize-package-specifier-3.0.3.tgz", + "integrity": "sha1-0N74gpUrjeP2frpekRmWYScfQfQ=" }, "request": { "version": "2.81.0", @@ -1064,7 +1069,8 @@ "dependencies": { "unique-slug": { "version": "2.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=" } } }, @@ -1420,7 +1426,8 @@ }, "spdx-license-ids": { "version": "1.2.0", - "bundled": true + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.0.tgz", + "integrity": "sha1-tUndD2Pct0Whfi6joHQC4OMy0eI=" } } } diff --git a/app/package.json b/app/package.json index b9a7cd69f..2a01c01c7 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "LBRY", - "version": "0.12.2rc3", + "version": "0.12.2rc5", "main": "main.js", "description": "LBRY is a fully decentralized, open-source protocol facilitating the discovery, access, and (sometimes) purchase of data.", "author": { diff --git a/build/DAEMON_URL b/build/DAEMON_URL index 3df4e528f..b3b581882 100644 --- a/build/DAEMON_URL +++ b/build/DAEMON_URL @@ -1 +1 @@ -https://github.com/lbryio/lbry/releases/download/v0.13.1rc1/lbrynet-daemon-v0.13.1rc1-OSNAME.zip +https://github.com/lbryio/lbry/releases/download/v0.13.1/lbrynet-daemon-v0.13.1-OSNAME.zip diff --git a/build/build.ps1 b/build/build.ps1 index 5add85a3a..0199853e0 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -13,6 +13,13 @@ cd .. # build ui cd ui npm install + +# necessary to ensure native Node modules (e.g. keytar) are built against the correct version of Node) +# yes, it needs to be run twice. it fails the first time, not sure why +node_modules\.bin\electron-rebuild +node_modules\.bin\electron-rebuild + +node extractLocals.js node_modules\.bin\node-sass --output dist\css --sourcemap=none scss\ node_modules\.bin\webpack Copy-Item dist ..\app\ -recurse diff --git a/build/build.sh b/build/build.sh index e3a6fffe6..9019a88be 100755 --- a/build/build.sh +++ b/build/build.sh @@ -45,6 +45,13 @@ if [ "$FULL_BUILD" == "true" ]; then python "$BUILD_DIR/set_version.py" fi +libsecret="libsecret-1-dev" +if $LINUX && [ -z "$(dpkg-query --show --showformat='${Status}\n' "$libsecret" 2>/dev/null | grep "install ok installed")" ]; then + # this is needed for keytar, which does secure password/token management + sudo apt-get install --no-install-recommends -y "$libsecret" +fi + + [ -d "$ROOT/dist" ] && rm -rf "$ROOT/dist" mkdir -p "$ROOT/dist" [ -d "$ROOT/app/dist" ] && rm -rf "$ROOT/app/dist" @@ -61,6 +68,16 @@ npm install ( cd "$ROOT/ui" npm install + + # necessary to ensure native Node modules (e.g. keytar) are built against the correct version of Node) + # yes, it needs to be run twice. it fails the first time, not sure why + set +e + # DEBUG=electron-rebuild node_modules/.bin/electron-rebuild . + node_modules/.bin/electron-rebuild "$ROOT/ui" + set -e + node_modules/.bin/electron-rebuild "$ROOT/ui" + + node extractLocals.js node_modules/.bin/node-sass --output dist/css --sourcemap=none scss/ node_modules/.bin/webpack cp -r dist/* "$ROOT/app/dist/" diff --git a/package-lock.json b/package-lock.json index 4e178ac4f..4b354044d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,6 @@ { "lockfileVersion": 1, "dependencies": { - "@types/node": { - "version": "7.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.31.tgz", - "integrity": "sha512-+KrE1LDddn97ip+gXZAnzNQ0pupKH/6tcKwTpo96BDVNpzmhIKGHug0Wd3H0dN4WEqYB1tXYI5m2mZuIZNI8tg==", - "dev": true - }, "7zip-bin": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-2.1.0.tgz", diff --git a/ui/.gitignore b/ui/.gitignore deleted file mode 100644 index 789fcf4f3..000000000 --- a/ui/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -dist/css/* -dist/js/* -!dist/js/mediaelement -node_modules -.sass-cache -.idea -.DS_Store diff --git a/ui/.travis.yml b/ui/.travis.yml deleted file mode 100644 index b1ad37013..000000000 --- a/ui/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -language: node_js -node_js: 5 - -# note that travis still builds PRs so that covers -# other branches -branches: - only: - - master - - development - -install: - - rvm install 2.3.1 - - npm install - -script: - - rvm use 2.3.1 && gem install danger --version '~> 4.0' && danger - - mkdir -p dist/css dist/js - - node_modules/.bin/node-sass scss/all.scss dist/css/all.css - - node_modules/.bin/webpack - - mkdir upload - - cd dist; zip -r ../upload/dist.zip *; cd - - - .travis/echo_sha.sh > upload/data.json - -deploy: - provider: s3 - access_key_id: - secure: "LjqvQ2MQj8fbumcnIKZC5wa03s3SIAnaLdOrmi7uRcltKIMko20QluRlpXipmYLdDRBg9sZrGgHJzN2QMZBk29XDsPhnFTG5Mhnaf9Ycxac2dy6FABtarNXjEOSH6Vv+Z96W8NkNTly7Okgyg0lXylTi1rLm/JxW14z+SrDPBOVPx5i4Wemd/y4Qx/5Oy611jaZIC4tExL+KWLCfX6tCgg1iE1vnJ22DAjNVlUOmZrLpuhuz/d/4mgkjtZvHAENDPmTItoN6WXxWPspMuaBaSMsj5OuhXqfro8BygbalWfUN7bSMvCqvO+irC6PBeZ4ntq675fS6g9Xb0vzZ6KnNck40PdHXdZRb4wvseqphCL4Xy9cLgUfND3GhxDtStJlqpMeaCg5Yjd9JknQjfCteObLqVeHyjIbWUNsQlyyQcgrVdAQRQiImG+do5qWQXC/02YqxffGJxW/6iQUC/TqC6vNoTKbMSVlccHExt9wnoooZ8D7MT7oN4BCk8bw+G/momH03hGoI0xffcVM9mN+y4z3/BbDX+bn/H5J2cmGWVFZHcOcbY+ehsTmdcqmm+KeePIpKmP6bD7waSUp+czXMgAgfjlWKuzmOFVxvlTo/rLzj5NlIH35RtIKOoVjU8kHzYI1aWAKIHNTHHrUJp4aVYeYGw5y2oejaHnO0ltmVoog=" - secret_access_key: - secure: "cAWUhKK+2//JBmV2ohcLs3WTDgDWAMK2RxDrwTqUBEeOdAHMRnEVR++a7fzEL+2dcYi939ZaynNXS5JFJANdjIEKmSWDtvyszyl5MWH0xERZtD0K0xs9bNXS8Un6UrTxhaatmQmqx0KokdUmdH5iqB02BBCKLuwGEjMcUVbadXkp+F8Pd4EXmDPbYHQ2eNo87FzQ7dCQ+GOyQBDNM1Orq+hOKiqJOzB9sDWKShsMTeq5Hyb1oji3cAxTyi6niJ/M/46NokLrPT0xrJLxy+sw5k2106SJXyofJsW1UVOsa5Nxx/suJTv2D0E6d7Ei1XYNnkAbDltu1m9wMq2xqLyoxfdvcC7bxr/NcUZ+DTCKcsM5oa8CiL/y+sOpB8ia0CjO3WBebIUWQAhyswQb312rYsbfQXscb5TnheS+wo4Z1MLse7fSm6jzZRZwqGi27GxC7SFWkSkBwHgaQ8tA7mzsdi6DFmz/KrnwgzqzFRkGg9d0RNXVfUN09p0GJr9drVS1DscVYWGhhD1eDKAIEaEd89u0vKF83zLk73UuNVZA38kkERtLwv7Yr1rRdftL3/gt6fMiW//JQB9sRWV0fDfiWbV2vaki6c/0s6moX00UBuZq21FEpx8puylUOwkXNlQK6TH/bw41ikrRMO156njokYOwLgQaebI/Mb0Cwi32gOU=" - bucket: lbry-ui - skip_cleanup: true - upload-dir: ${TRAVIS_BRANCH} - local_dir: upload - on: - all_branches: true diff --git a/ui/.travis/echo_sha.sh b/ui/.travis/echo_sha.sh deleted file mode 100755 index e3ca8d762..000000000 --- a/ui/.travis/echo_sha.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# this is a script instead of a line in the travis.yml -# getting the quoting on this correctly inside the yaml is -# unreadable and difficult - -echo '{"sha": "'${TRAVIS_COMMIT}'"}' diff --git a/ui/LICENSE.md b/ui/LICENSE.md deleted file mode 100644 index ee8b246ea..000000000 --- a/ui/LICENSE.md +++ /dev/null @@ -1 +0,0 @@ -See https://github.com/lbryio/lbry/blob/master/LICENSE diff --git a/ui/extractLocals.js b/ui/extractLocals.js new file mode 100644 index 000000000..b36e3e9d4 --- /dev/null +++ b/ui/extractLocals.js @@ -0,0 +1,48 @@ +var extract = require("i18n-extract"); +const fs = require("fs"); + +var dir = __dirname + "/../app/dist/locales"; +var path = dir + "/en.json"; + +if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); +} + +fs.writeFile(path, "{}", "utf8", function(err) { + if (err) { + return console.log(err); + } + var enLocale = require(path); + + const keys = extract.extractFromFiles(["js/**/*.{js,jsx}"], { + marker: "__", + }); + + let reports = []; + reports = reports.concat(extract.findMissing(enLocale, keys)); + + if (reports.length > 0) { + fs.readFile(path, "utf8", function readFileCallback(err, data) { + if (err) { + console.log(err); + } else { + localeObj = JSON.parse(data); + + for (var i = 0; i < reports.length; i++) { + // no need to care for other types than MISSING because starting file will always be empty + if (reports[i].type === "MISSING") { + localeObj[reports[i].key] = reports[i].key; + } + } + + var json = JSON.stringify(localeObj, null, "\t"); //convert it back to json-string + fs.writeFile(path, json, "utf8", function callback(err) { + if (err) { + throw err; + } + console.log("Extracted all strings!"); + }); + } + }); + } +}); diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js index d82c51606..9657b8756 100644 --- a/ui/js/actions/app.js +++ b/ui/js/actions/app.js @@ -225,7 +225,6 @@ export function doDaemonReady() { dispatch({ type: types.DAEMON_READY, }); - dispatch(doChangePath("/discover")); dispatch(doFetchDaemonSettings()); dispatch(doFileList()); }; diff --git a/ui/js/actions/user.js b/ui/js/actions/user.js index 972e167eb..662521617 100644 --- a/ui/js/actions/user.js +++ b/ui/js/actions/user.js @@ -33,20 +33,20 @@ export function doUserFetch() { dispatch({ type: types.USER_FETCH_STARTED, }); - lbryio.setCurrentUser( - user => { + lbryio + .getCurrentUser() + .then(user => { dispatch({ type: types.USER_FETCH_SUCCESS, data: { user }, }); - }, - error => { + }) + .catch(error => { dispatch({ type: types.USER_FETCH_FAILURE, data: { error }, }); - } - ); + }); }; } @@ -56,32 +56,37 @@ export function doUserEmailNew(email) { type: types.USER_EMAIL_NEW_STARTED, email: email, }); - lbryio.call("user_email", "new", { email }, "post").then( - () => { + lbryio + .call( + "user_email", + "new", + { email: email, send_verification_email: true }, + "post" + ) + .catch(error => { + if (error.xhr && error.xhr.status == 409) { + return lbryio.call( + "user_email", + "resend_token", + { email: email, only_if_expired: true }, + "post" + ); + } + throw error; + }) + .then(() => { dispatch({ type: types.USER_EMAIL_NEW_SUCCESS, data: { email }, }); dispatch(doUserFetch()); - }, - error => { - if ( - error.xhr && - (error.xhr.status == 409 || - error.message == "This email is already in use") - ) { - dispatch({ - type: types.USER_EMAIL_NEW_EXISTS, - data: { email }, - }); - } else { - dispatch({ - type: types.USER_EMAIL_NEW_FAILURE, - data: { error: error.message }, - }); - } - } - ); + }) + .catch(error => { + dispatch({ + type: types.USER_EMAIL_NEW_FAILURE, + data: { error: error.message }, + }); + }); }; } diff --git a/ui/js/app.js b/ui/js/app.js index 1f1f32ecd..41701ee18 100644 --- a/ui/js/app.js +++ b/ui/js/app.js @@ -1,27 +1,27 @@ -import store from 'store.js'; -import lbry from './lbry.js'; +import store from "store.js"; +import lbry from "./lbry.js"; const env = ENV; const config = require(`./config/${env}`); -const language = lbry.getClientSetting('language') - ? lbry.getClientSetting('language') - : 'en'; -const i18n = require('y18n')({ - directory: 'app/locales', - updateFiles: false, - locale: language +const language = lbry.getClientSetting("language") + ? lbry.getClientSetting("language") + : "en"; +const i18n = require("y18n")({ + directory: "app/locales", + updateFiles: false, + locale: language, }); const logs = []; const app = { - env: env, - config: config, - store: store, - i18n: i18n, - logs: logs, - log: function(message) { - console.log(message); - logs.push(message); - } + env: env, + config: config, + store: store, + i18n: i18n, + logs: logs, + log: function(message) { + console.log(message); + logs.push(message); + }, }; window.__ = i18n.__; diff --git a/ui/js/component/filePrice/index.js b/ui/js/component/filePrice/index.js index aa8f692aa..be675d924 100644 --- a/ui/js/component/filePrice/index.js +++ b/ui/js/component/filePrice/index.js @@ -4,11 +4,9 @@ import { doFetchCostInfoForUri } from "actions/cost_info"; import { makeSelectCostInfoForUri, makeSelectFetchingCostInfoForUri, -} from 'selectors/cost_info' -import { - makeSelectClaimForUri, -} from 'selectors/claims' -import FilePrice from './view' +} from "selectors/cost_info"; +import { makeSelectClaimForUri } from "selectors/claims"; +import FilePrice from "./view"; const makeSelect = () => { const selectCostInfoForUri = makeSelectCostInfoForUri(); diff --git a/ui/js/component/rewardLink/view.jsx b/ui/js/component/rewardLink/view.jsx index 6ebd50a20..be3a72f35 100644 --- a/ui/js/component/rewardLink/view.jsx +++ b/ui/js/component/rewardLink/view.jsx @@ -21,7 +21,7 @@ const RewardLink = props => { : { claimReward(reward); }} diff --git a/ui/js/component/userEmailVerify/view.jsx b/ui/js/component/userEmailVerify/view.jsx index c6cc65f34..5aa656a61 100644 --- a/ui/js/component/userEmailVerify/view.jsx +++ b/ui/js/component/userEmailVerify/view.jsx @@ -34,7 +34,7 @@ class UserEmailVerify extends React.PureComponent { >

- Email if - you did not receive or are having trouble with your code. + {__("Email")}{" "} + {" "} + {__("if you did not receive or are having trouble with your code.")}

{ this.handleSubmit(event); diff --git a/ui/js/component/walletSend/view.jsx b/ui/js/component/walletSend/view.jsx index 31fd7d911..4d4de0fd6 100644 --- a/ui/js/component/walletSend/view.jsx +++ b/ui/js/component/walletSend/view.jsx @@ -23,7 +23,7 @@ const WalletSend = props => {
-

Welcome to LBRY.

+

{__("Welcome to LBRY.")}

- Using LBRY is like dating a centaur. Totally normal up top, and - {" "}way different underneath. + {__( + "Using LBRY is like dating a centaur. Totally normal up top, and" + )} + {" "}{__("way different")} {__("underneath.")}

-

Up top, LBRY is similar to popular media sites.

+

{__("Up top, LBRY is similar to popular media sites.")}

- Below, LBRY is controlled by users -- you -- via blockchain and - decentralization. + {__( + "Below, LBRY is controlled by users -- you -- via blockchain and decentralization." + )}

- Thank you for making content freedom possible! + {__("Thank you for making content freedom possible!")} {" "}{isRewardApproved ? __("Here's a nickel, kid.") : ""}

@@ -31,7 +34,7 @@ class WelcomeModal extends React.PureComponent { : }
@@ -40,32 +43,35 @@ class WelcomeModal extends React.PureComponent { type="alert" overlayClassName="modal-overlay modal-overlay--clear" isOpen={true} - contentLabel="Welcome to LBRY" + contentLabel={__("Welcome to LBRY")} onConfirmed={closeModal} >
-

About Your Reward

+

{__("About Your Reward")}

- You earned a reward of + {__("You earned a reward of")} {" "} - {" "}LBRY - credits, or LBC. + {" "}{__("LBRY credits, or")} {__("LBC")}.

- This reward will show in your Wallet momentarily, probably while - you are reading this message. + {__( + "This reward will show in your Wallet momentarily, probably while you are reading this message." + )}

- LBC is used to compensate creators, to publish, and to have say in - how the network works. + {__( + "LBC is used to compensate creators, to publish, and to have say in how the network works." + )}

- No need to understand it all just yet! Try watching or downloading - something next. + {__( + "No need to understand it all just yet! Try watching or downloading something next." + )}

- Finally, know that LBRY is an early beta and that it earns the - name. + {__( + "Finally, know that LBRY is an early beta and that it earns the name." + )}

; diff --git a/ui/js/jsonrpc.js b/ui/js/jsonrpc.js index b07dbe547..d401d2eaf 100644 --- a/ui/js/jsonrpc.js +++ b/ui/js/jsonrpc.js @@ -1,87 +1,87 @@ const jsonrpc = {}; jsonrpc.call = function( - connectionString, - method, - params, - callback, - errorCallback, - connectFailedCallback, - timeout + connectionString, + method, + params, + callback, + errorCallback, + connectFailedCallback, + timeout ) { - var xhr = new XMLHttpRequest(); - if (typeof connectFailedCallback !== 'undefined') { - if (timeout) { - xhr.timeout = timeout; - } + var xhr = new XMLHttpRequest(); + if (typeof connectFailedCallback !== "undefined") { + if (timeout) { + xhr.timeout = timeout; + } - xhr.addEventListener('error', function(e) { - connectFailedCallback(e); - }); - xhr.addEventListener('timeout', function() { - connectFailedCallback( - new Error(__('XMLHttpRequest connection timed out')) - ); - }); - } - xhr.addEventListener('load', function() { - var response = JSON.parse(xhr.responseText); + xhr.addEventListener("error", function(e) { + connectFailedCallback(e); + }); + xhr.addEventListener("timeout", function() { + connectFailedCallback( + new Error(__("XMLHttpRequest connection timed out")) + ); + }); + } + xhr.addEventListener("load", function() { + var response = JSON.parse(xhr.responseText); - if (response.error) { - if (errorCallback) { - errorCallback(response.error); - } else { - var errorEvent = new CustomEvent('unhandledError', { - detail: { - connectionString: connectionString, - method: method, - params: params, - code: response.error.code, - message: response.error.message, - data: response.error.data - } - }); - document.dispatchEvent(errorEvent); - } - } else if (callback) { - callback(response.result); - } - }); + if (response.error) { + if (errorCallback) { + errorCallback(response.error); + } else { + var errorEvent = new CustomEvent("unhandledError", { + detail: { + connectionString: connectionString, + method: method, + params: params, + code: response.error.code, + message: response.error.message, + data: response.error.data, + }, + }); + document.dispatchEvent(errorEvent); + } + } else if (callback) { + callback(response.result); + } + }); - if (connectFailedCallback) { - xhr.addEventListener('error', function(event) { - connectFailedCallback(event); - }); - } else { - xhr.addEventListener('error', function(event) { - var errorEvent = new CustomEvent('unhandledError', { - detail: { - connectionString: connectionString, - method: method, - params: params, - code: xhr.status, - message: __('Connection to API server failed') - } - }); - document.dispatchEvent(errorEvent); - }); - } + if (connectFailedCallback) { + xhr.addEventListener("error", function(event) { + connectFailedCallback(event); + }); + } else { + xhr.addEventListener("error", function(event) { + var errorEvent = new CustomEvent("unhandledError", { + detail: { + connectionString: connectionString, + method: method, + params: params, + code: xhr.status, + message: __("Connection to API server failed"), + }, + }); + document.dispatchEvent(errorEvent); + }); + } - const counter = parseInt(sessionStorage.getItem('JSONRPCCounter') || 0); + const counter = parseInt(sessionStorage.getItem("JSONRPCCounter") || 0); - xhr.open('POST', connectionString, true); - xhr.send( - JSON.stringify({ - jsonrpc: '2.0', - method: method, - params: params, - id: counter - }) - ); + xhr.open("POST", connectionString, true); + xhr.send( + JSON.stringify({ + jsonrpc: "2.0", + method: method, + params: params, + id: counter, + }) + ); - sessionStorage.setItem('JSONRPCCounter', counter + 1); + sessionStorage.setItem("JSONRPCCounter", counter + 1); - return xhr; + return xhr; }; export default jsonrpc; diff --git a/ui/js/lbry.js b/ui/js/lbry.js index 3f4feecb7..286218532 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -319,11 +319,11 @@ lbry.getMediaType = function(contentType, fileName) { } var ext = fileName.substr(dotIndex + 1); - if (/^mp4|mov|m4v|flv|f4v$/i.test(ext)) { + if (/^mp4|m4v|mov|webm|flv|f4v|ogv$/i.test(ext)) { return "video"; - } else if (/^mp3|m4a|aac|wav|flac|ogg$/i.test(ext)) { + } else if (/^mp3|m4a|aac|wav|flac|ogg|opus$/i.test(ext)) { return "audio"; - } else if (/^html|htm|pdf|odf|doc|docx|md|markdown|txt$/i.test(ext)) { + } else if (/^html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|org$/i.test(ext)) { return "document"; } else { return "unknown"; @@ -417,7 +417,7 @@ lbry.file_list = function(params = {}) { return; } } - + apiCall( "file_list", params, @@ -458,7 +458,6 @@ lbry.claim_list_mine = function(params = {}) { }); }; - lbry._resolveXhrs = {}; lbry.resolve = function(params = {}) { return new Promise((resolve, reject) => { diff --git a/ui/js/lbryio.js b/ui/js/lbryio.js index 11416fbd6..a371d4db7 100644 --- a/ui/js/lbryio.js +++ b/ui/js/lbryio.js @@ -1,21 +1,21 @@ -import { getSession, setSession, setLocal } from "./utils.js"; import lbry from "./lbry.js"; const querystring = require("querystring"); +const keytar = require("keytar"); const lbryio = { - _accessToken: getSession("accessToken"), - _authenticationPromise: null, enabled: true, + _authenticationPromise: null, + _exchangePromise: null, + _exchangeLastFetched: null, }; const CONNECTION_STRING = process.env.LBRY_APP_API_URL ? process.env.LBRY_APP_API_URL.replace(/\/*$/, "/") // exactly one slash at the end : "https://api.lbry.io/"; + const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000; -lbryio._exchangePromise = null; -lbryio._exchangeLastFetched = null; lbryio.getExchangeRates = function() { if ( !lbryio._exchangeLastFetched || @@ -60,7 +60,7 @@ lbryio.call = function(resource, action, params = {}, method = "get") { if (!response.success) { if (reject) { - let error = new Error(response.error); + const error = new Error(response.error); error.xhr = xhr; reject(error); } else { @@ -81,54 +81,54 @@ lbryio.call = function(resource, action, params = {}, method = "get") { } }); - // For social media auth: - //const accessToken = localStorage.getItem('accessToken'); - //const fullParams = {...params, ... accessToken ? {access_token: accessToken} : {}}; + lbryio + .getAuthToken() + .then(token => { + const fullParams = { auth_token: token, ...params }; - // Temp app ID based auth: - const fullParams = { app_id: lbryio.getAccessToken(), ...params }; - - if (method == "get") { - xhr.open( - "get", - CONNECTION_STRING + - resource + - "/" + - action + - "?" + - querystring.stringify(fullParams), - true - ); - xhr.send(); - } else if (method == "post") { - xhr.open("post", CONNECTION_STRING + resource + "/" + action, true); - xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhr.send(querystring.stringify(fullParams)); - } else { - reject(new Error(__("Invalid method"))); - } + if (method == "get") { + xhr.open( + "get", + CONNECTION_STRING + + resource + + "/" + + action + + "?" + + querystring.stringify(fullParams), + true + ); + xhr.send(); + } else if (method == "post") { + xhr.open("post", CONNECTION_STRING + resource + "/" + action, true); + xhr.setRequestHeader( + "Content-type", + "application/x-www-form-urlencoded" + ); + xhr.send(querystring.stringify(fullParams)); + } else { + reject(new Error(__("Invalid method"))); + } + }) + .catch(reject); }); }; -lbryio.getAccessToken = () => { - const token = getSession("accessToken"); - return token ? token.toString().trim() : token; +lbryio.getAuthToken = () => { + return keytar.getPassword("LBRY", "auth_token").then(token => { + return token ? token.toString().trim() : null; + }); }; -lbryio.setAccessToken = token => { - setSession("accessToken", token ? token.toString().trim() : token); +lbryio.setAuthToken = token => { + return keytar.setPassword( + "LBRY", + "auth_token", + token ? token.toString().trim() : null + ); }; -lbryio.setCurrentUser = (resolve, reject) => { - lbryio - .call("user", "me") - .then(data => { - resolve(data); - }) - .catch(function(err) { - lbryio.setAccessToken(null); - reject(err); - }); +lbryio.getCurrentUser = () => { + return lbryio.call("user", "me"); }; lbryio.authenticate = function() { @@ -144,48 +144,69 @@ lbryio.authenticate = function() { }); }); } + if (lbryio._authenticationPromise === null) { lbryio._authenticationPromise = new Promise((resolve, reject) => { - lbry - .status() - .then(response => { - let installation_id = response.installation_id; - - if (!lbryio.getAccessToken()) { - lbryio - .call( - "user", - "new", - { - language: "en", - app_id: installation_id, - }, - "post" - ) - .then(function(responseData) { - if (!responseData.id) { - reject( - new Error("Received invalid authentication response.") - ); - } - lbryio.setAccessToken(installation_id); - lbryio.setCurrentUser(resolve, reject); - }) - .catch(function(error) { - /* - until we have better error code format, assume all errors are duplicate application id - if we're wrong, this will be caught by later attempts to make a valid call - */ - lbryio.setAccessToken(installation_id); - lbryio.setCurrentUser(resolve, reject); - }); - } else { - lbryio.setCurrentUser(resolve, reject); + lbryio + .getAuthToken() + .then(token => { + if (!token || token.length > 60) { + return false; } + + // check that token works + return lbryio + .getCurrentUser() + .then(() => { + return true; + }) + .catch(() => { + return false; + }); }) - .catch(reject); + .then(isTokenValid => { + if (isTokenValid) { + 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 }, + "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")); + } + return lbryio.setAuthToken(response.auth_token); + }); + }) + .then(lbryio.getCurrentUser()) + .then(resolve, reject); }); } + return lbryio._authenticationPromise; }; diff --git a/ui/js/lbryuri.js b/ui/js/lbryuri.js index fce46d217..b58ad55ac 100644 --- a/ui/js/lbryuri.js +++ b/ui/js/lbryuri.js @@ -26,125 +26,125 @@ const lbryuri = {}; * - channelName (string, if present): Channel name without @ */ lbryuri.parse = function(uri, requireProto = false) { - // Break into components. Empty sub-matches are converted to null - const componentsRegex = new RegExp( - '^((?:lbry://)?)' + // protocol - '([^:$#/]*)' + // name (stops at the first separator or end) - '([:$#]?)([^/]*)' + // modifier separator, modifier (stops at the first path separator or end) - '(/?)(.*)' // path separator, path - ); - const [proto, name, modSep, modVal, pathSep, path] = componentsRegex - .exec(uri) - .slice(1) - .map(match => match || null); + // Break into components. Empty sub-matches are converted to null + const componentsRegex = new RegExp( + "^((?:lbry://)?)" + // protocol + "([^:$#/]*)" + // name (stops at the first separator or end) + "([:$#]?)([^/]*)" + // modifier separator, modifier (stops at the first path separator or end) + "(/?)(.*)" // path separator, path + ); + const [proto, name, modSep, modVal, pathSep, path] = componentsRegex + .exec(uri) + .slice(1) + .map(match => match || null); - let contentName; + let contentName; - // Validate protocol - if (requireProto && !proto) { - throw new Error(__('LBRY URIs must include a protocol prefix (lbry://).')); - } + // Validate protocol + if (requireProto && !proto) { + throw new Error(__("LBRY URIs must include a protocol prefix (lbry://).")); + } - // Validate and process name - if (!name) { - throw new Error(__('URI does not include name.')); - } + // Validate and process name + if (!name) { + throw new Error(__("URI does not include name.")); + } - const isChannel = name.startsWith('@'); - const channelName = isChannel ? name.slice(1) : name; + const isChannel = name.startsWith("@"); + const channelName = isChannel ? name.slice(1) : name; - if (isChannel) { - if (!channelName) { - throw new Error(__('No channel name after @.')); - } + if (isChannel) { + if (!channelName) { + throw new Error(__("No channel name after @.")); + } - if (channelName.length < CHANNEL_NAME_MIN_LEN) { - throw new Error( - __( - `Channel names must be at least %s characters.`, - CHANNEL_NAME_MIN_LEN - ) - ); - } + if (channelName.length < CHANNEL_NAME_MIN_LEN) { + throw new Error( + __( + `Channel names must be at least %s characters.`, + CHANNEL_NAME_MIN_LEN + ) + ); + } - contentName = path; - } + contentName = path; + } - const nameBadChars = (channelName || name).match(/[^A-Za-z0-9-]/g); - if (nameBadChars) { - throw new Error( - __( - `Invalid character %s in name: %s.`, - nameBadChars.length == 1 ? '' : 's', - nameBadChars.join(', ') - ) - ); - } + const nameBadChars = (channelName || name).match(/[^A-Za-z0-9-]/g); + if (nameBadChars) { + throw new Error( + __( + `Invalid character %s in name: %s.`, + nameBadChars.length == 1 ? "" : "s", + nameBadChars.join(", ") + ) + ); + } - // Validate and process modifier (claim ID, bid position or claim sequence) - let claimId, claimSequence, bidPosition; - if (modSep) { - if (!modVal) { - throw new Error(__(`No modifier provided after separator %s.`, modSep)); - } + // Validate and process modifier (claim ID, bid position or claim sequence) + let claimId, claimSequence, bidPosition; + if (modSep) { + if (!modVal) { + throw new Error(__(`No modifier provided after separator %s.`, modSep)); + } - if (modSep == '#') { - claimId = modVal; - } else if (modSep == ':') { - claimSequence = modVal; - } else if (modSep == '$') { - bidPosition = modVal; - } - } + if (modSep == "#") { + claimId = modVal; + } else if (modSep == ":") { + claimSequence = modVal; + } else if (modSep == "$") { + bidPosition = modVal; + } + } - if ( - claimId && - (claimId.length > CLAIM_ID_MAX_LEN || !claimId.match(/^[0-9a-f]+$/)) - ) { - throw new Error(__(`Invalid claim ID %s.`, claimId)); - } + if ( + claimId && + (claimId.length > CLAIM_ID_MAX_LEN || !claimId.match(/^[0-9a-f]+$/)) + ) { + throw new Error(__(`Invalid claim ID %s.`, claimId)); + } - if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) { - throw new Error(__('Claim sequence must be a number.')); - } + if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) { + throw new Error(__("Claim sequence must be a number.")); + } - if (bidPosition && !bidPosition.match(/^-?[1-9][0-9]*$/)) { - throw new Error(__('Bid position must be a number.')); - } + if (bidPosition && !bidPosition.match(/^-?[1-9][0-9]*$/)) { + throw new Error(__("Bid position must be a number.")); + } - // Validate and process path - if (path) { - if (!isChannel) { - throw new Error(__('Only channel URIs may have a path.')); - } + // Validate and process path + if (path) { + if (!isChannel) { + throw new Error(__("Only channel URIs may have a path.")); + } - const pathBadChars = path.match(/[^A-Za-z0-9-]/g); - if (pathBadChars) { - throw new Error( - __( - `Invalid character %s in path: %s`, - count == 1 ? '' : 's', - nameBadChars.join(', ') - ) - ); - } + const pathBadChars = path.match(/[^A-Za-z0-9-]/g); + if (pathBadChars) { + throw new Error( + __( + `Invalid character %s in path: %s`, + count == 1 ? "" : "s", + nameBadChars.join(", ") + ) + ); + } - contentName = path; - } else if (pathSep) { - throw new Error(__('No path provided after /')); - } + contentName = path; + } else if (pathSep) { + throw new Error(__("No path provided after /")); + } - return { - name, - path, - isChannel, - ...(contentName ? { contentName } : {}), - ...(channelName ? { channelName } : {}), - ...(claimSequence ? { claimSequence: parseInt(claimSequence) } : {}), - ...(bidPosition ? { bidPosition: parseInt(bidPosition) } : {}), - ...(claimId ? { claimId } : {}), - ...(path ? { path } : {}) - }; + return { + name, + path, + isChannel, + ...(contentName ? { contentName } : {}), + ...(channelName ? { channelName } : {}), + ...(claimSequence ? { claimSequence: parseInt(claimSequence) } : {}), + ...(bidPosition ? { bidPosition: parseInt(bidPosition) } : {}), + ...(claimId ? { claimId } : {}), + ...(path ? { path } : {}), + }; }; /** @@ -153,96 +153,96 @@ lbryuri.parse = function(uri, requireProto = false) { * The channelName key will accept names with or without the @ prefix. */ lbryuri.build = function(uriObj, includeProto = true, allowExtraProps = false) { - let { - name, - claimId, - claimSequence, - bidPosition, - path, - contentName, - channelName - } = uriObj; + let { + name, + claimId, + claimSequence, + bidPosition, + path, + contentName, + channelName, + } = uriObj; - if (channelName) { - const channelNameFormatted = channelName.startsWith('@') - ? channelName - : '@' + channelName; - if (!name) { - name = channelNameFormatted; - } else if (name !== channelNameFormatted) { - throw new Error( - __( - 'Received a channel content URI, but name and channelName do not match. "name" represents the value in the name position of the URI (lbry://name...), which for channel content will be the channel name. In most cases, to construct a channel URI you should just pass channelName and contentName.' - ) - ); - } - } + if (channelName) { + const channelNameFormatted = channelName.startsWith("@") + ? channelName + : "@" + channelName; + if (!name) { + name = channelNameFormatted; + } else if (name !== channelNameFormatted) { + throw new Error( + __( + 'Received a channel content URI, but name and channelName do not match. "name" represents the value in the name position of the URI (lbry://name...), which for channel content will be the channel name. In most cases, to construct a channel URI you should just pass channelName and contentName.' + ) + ); + } + } - if (contentName) { - if (!name) { - name = contentName; - } else if (!path) { - path = contentName; - } - if (path && path !== contentName) { - throw new Error( - __( - 'Path and contentName do not match. Only one is required; most likely you wanted contentName.' - ) - ); - } - } + if (contentName) { + if (!name) { + name = contentName; + } else if (!path) { + path = contentName; + } + if (path && path !== contentName) { + throw new Error( + __( + "Path and contentName do not match. Only one is required; most likely you wanted contentName." + ) + ); + } + } - return ( - (includeProto ? 'lbry://' : '') + - name + - (claimId ? `#${claimId}` : '') + - (claimSequence ? `:${claimSequence}` : '') + - (bidPosition ? `\$${bidPosition}` : '') + - (path ? `/${path}` : '') - ); + return ( + (includeProto ? "lbry://" : "") + + name + + (claimId ? `#${claimId}` : "") + + (claimSequence ? `:${claimSequence}` : "") + + (bidPosition ? `\$${bidPosition}` : "") + + (path ? `/${path}` : "") + ); }; /* Takes a parseable LBRY URI and converts it to standard, canonical format (currently this just * consists of adding the lbry:// prefix if needed) */ lbryuri.normalize = function(uri) { - const { name, path, bidPosition, claimSequence, claimId } = lbryuri.parse( - uri - ); - return lbryuri.build({ name, path, claimSequence, bidPosition, claimId }); + const { name, path, bidPosition, claimSequence, claimId } = lbryuri.parse( + uri + ); + return lbryuri.build({ name, path, claimSequence, bidPosition, claimId }); }; lbryuri.isValid = function(uri) { - let parts; - try { - parts = lbryuri.parse(lbryuri.normalize(uri)); - } catch (error) { - return false; - } - return parts && parts.name; + let parts; + try { + parts = lbryuri.parse(lbryuri.normalize(uri)); + } catch (error) { + return false; + } + return parts && parts.name; }; lbryuri.isValidName = function(name, checkCase = true) { - const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i'); - return regexp.test(name); + const regexp = new RegExp("^[a-z0-9-]+$", checkCase ? "" : "i"); + return regexp.test(name); }; lbryuri.isClaimable = function(uri) { - let parts; - try { - parts = lbryuri.parse(lbryuri.normalize(uri)); - } catch (error) { - return false; - } - return ( - parts && - parts.name && - !parts.claimId && - !parts.bidPosition && - !parts.claimSequence && - !parts.isChannel && - !parts.path - ); + let parts; + try { + parts = lbryuri.parse(lbryuri.normalize(uri)); + } catch (error) { + return false; + } + return ( + parts && + parts.name && + !parts.claimId && + !parts.bidPosition && + !parts.claimSequence && + !parts.isChannel && + !parts.path + ); }; window.lbryuri = lbryuri; diff --git a/ui/js/lighthouse.js b/ui/js/lighthouse.js index cfac14a9a..d7052a0ee 100644 --- a/ui/js/lighthouse.js +++ b/ui/js/lighthouse.js @@ -1,84 +1,84 @@ -import lbry from './lbry.js'; -import jsonrpc from './jsonrpc.js'; +import lbry from "./lbry.js"; +import jsonrpc from "./jsonrpc.js"; const queryTimeout = 3000; const maxQueryTries = 2; const defaultServers = [ - 'http://lighthouse7.lbry.io:50005', - 'http://lighthouse8.lbry.io:50005', - 'http://lighthouse9.lbry.io:50005' + "http://lighthouse7.lbry.io:50005", + "http://lighthouse8.lbry.io:50005", + "http://lighthouse9.lbry.io:50005", ]; -const path = '/'; +const path = "/"; let server = null; let connectTryNum = 0; function getServers() { - return lbry.getClientSetting('useCustomLighthouseServers') - ? lbry.getClientSetting('customLighthouseServers') - : defaultServers; + return lbry.getClientSetting("useCustomLighthouseServers") + ? lbry.getClientSetting("customLighthouseServers") + : defaultServers; } function call(method, params, callback, errorCallback) { - if (connectTryNum >= maxQueryTries) { - errorCallback( - new Error( - __( - `Could not connect to Lighthouse server. Last server attempted: %s`, - server - ) - ) - ); - return; - } + if (connectTryNum >= maxQueryTries) { + errorCallback( + new Error( + __( + `Could not connect to Lighthouse server. Last server attempted: %s`, + server + ) + ) + ); + return; + } - /** + /** * Set the Lighthouse server if it hasn't been set yet, if the current server is not in current * set of servers (most likely because of a settings change), or we're re-trying after a failed * query. */ - if (!server || !getServers().includes(server) || connectTryNum > 0) { - // If there's a current server, filter it out so we get a new one - const newServerChoices = server - ? getServers().filter(s => s != server) - : getServers(); - server = - newServerChoices[ - Math.round(Math.random() * (newServerChoices.length - 1)) - ]; - } + if (!server || !getServers().includes(server) || connectTryNum > 0) { + // If there's a current server, filter it out so we get a new one + const newServerChoices = server + ? getServers().filter(s => s != server) + : getServers(); + server = + newServerChoices[ + Math.round(Math.random() * (newServerChoices.length - 1)) + ]; + } - jsonrpc.call( - server + path, - method, - params, - response => { - connectTryNum = 0; - callback(response); - }, - error => { - connectTryNum = 0; - errorCallback(error); - }, - () => { - connectTryNum++; - call(method, params, callback, errorCallback); - }, - queryTimeout - ); + jsonrpc.call( + server + path, + method, + params, + response => { + connectTryNum = 0; + callback(response); + }, + error => { + connectTryNum = 0; + errorCallback(error); + }, + () => { + connectTryNum++; + call(method, params, callback, errorCallback); + }, + queryTimeout + ); } const lighthouse = new Proxy( - {}, - { - get: function(target, name) { - return function(...params) { - return new Promise((resolve, reject) => { - call(name, params, resolve, reject); - }); - }; - } - } + {}, + { + get: function(target, name) { + return function(...params) { + return new Promise((resolve, reject) => { + call(name, params, resolve, reject); + }); + }; + }, + } ); export default lighthouse; diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx index ea51972eb..22f5f2b80 100644 --- a/ui/js/page/discover/view.jsx +++ b/ui/js/page/discover/view.jsx @@ -5,11 +5,6 @@ import FileCard from "component/fileCard"; import { BusyMessage } from "component/common.js"; import ToolTip from "component/tooltip.js"; -const communityCategoryToolTipText = - "Community Content is a public space where anyone can share content with the " + - 'rest of the LBRY community. Bid on the names "one," "two," "three," "four" and ' + - '"five" to put your content here!'; - const FeaturedCategory = props => { const { category, names } = props; @@ -21,7 +16,9 @@ const FeaturedCategory = props => { category.match(/^community/i) && } diff --git a/ui/js/page/publish/index.js b/ui/js/page/publish/index.js index cd01864d9..fac4419c1 100644 --- a/ui/js/page/publish/index.js +++ b/ui/js/page/publish/index.js @@ -15,7 +15,8 @@ const perform = dispatch => ({ back: () => dispatch(doHistoryBack()), navigate: path => dispatch(doNavigate(path)), fetchClaimListMine: () => dispatch(doFetchClaimListMine()), - claimFirstChannelReward: () => dispatch(doClaimRewardType(rewards.TYPE_FIRST_CHANNEL)), + claimFirstChannelReward: () => + dispatch(doClaimRewardType(rewards.TYPE_FIRST_CHANNEL)), }); export default connect(select, perform)(PublishPage); diff --git a/ui/js/page/publish/view.jsx b/ui/js/page/publish/view.jsx index 2628e01e8..7959ad9f7 100644 --- a/ui/js/page/publish/view.jsx +++ b/ui/js/page/publish/view.jsx @@ -433,17 +433,26 @@ class PublishPage extends React.PureComponent { "You have already used this URL. Publishing to it again will update your previous publish." ); } else if (this.state.topClaimValue) { - return ( - - {__n( - 'A deposit of at least "%s" credit is required to win "%s". However, you can still get a permanent URL for any amount.', - 'A deposit of at least "%s" credits is required to win "%s". However, you can still get a permanent URL for any amount.', - this.state.topClaimValue /*pluralization param*/, - this.state.topClaimValue, - this.state.name /*regular params*/ - )} - - ); + if (this.state.topClaimValue === 1) { + return ( + + {__( + 'A deposit of at least one credit is required to win "%s". However, you can still get a permanent URL for any amount.', + this.state.name + )} + + ); + } else { + return ( + + {__( + 'A deposit of at least "%s" credits is required to win "%s". However, you can still get a permanent URL for any amount.', + this.state.topClaimValue, + this.state.name + )} + + ); + } } else { return ""; } @@ -752,7 +761,7 @@ class PublishPage extends React.PureComponent { /> {
{claimed - ? Reward claimed. + ? {__("Reward claimed.")} : }
{reward.reward_description}
@@ -59,14 +59,14 @@ const RewardsPage = props => {

{__("You are not eligible to claim rewards.")}

- To become eligible, email - {" "} with a - link to a public social media profile. + {__("To become eligible, email")} + {" "}{" "} + {__("with a link to a public social media profile.")}

); } else if (fetching) { - content = ; + content = ; } else if (rewards.length > 0) { content = rewards.map(reward => diff --git a/ui/js/selectors/user.js b/ui/js/selectors/user.js index f7104d3d4..bd981273e 100644 --- a/ui/js/selectors/user.js +++ b/ui/js/selectors/user.js @@ -68,8 +68,7 @@ export const selectUserIsVerificationCandidate = createSelector( selectUserIsRewardApproved, selectEmailToVerify, selectUser, - (isEligible, isApproved, emailToVerify, user) => - (isEligible && !isApproved) || (emailToVerify && user && !user.has_email) + (isEligible, isApproved, emailToVerify, user) => emailToVerify && user ); export const selectUserIsAuthRequested = createSelector( diff --git a/ui/js/utils.js b/ui/js/utils.js index 3b3160f13..783f85113 100644 --- a/ui/js/utils.js +++ b/ui/js/utils.js @@ -3,15 +3,15 @@ * is not set yet. */ export function getLocal(key, fallback = undefined) { - const itemRaw = localStorage.getItem(key); - return itemRaw === null ? fallback : JSON.parse(itemRaw); + const itemRaw = localStorage.getItem(key); + return itemRaw === null ? fallback : JSON.parse(itemRaw); } /** * Thin wrapper around localStorage.setItem(). Converts value to JSON. */ export function setLocal(key, value) { - localStorage.setItem(key, JSON.stringify(value)); + localStorage.setItem(key, JSON.stringify(value)); } /** @@ -19,13 +19,13 @@ export function setLocal(key, value) { * is not set yet. */ export function getSession(key, fallback = undefined) { - const itemRaw = sessionStorage.getItem(key); - return itemRaw === null ? fallback : JSON.parse(itemRaw); + const itemRaw = sessionStorage.getItem(key); + return itemRaw === null ? fallback : JSON.parse(itemRaw); } /** * Thin wrapper around localStorage.setItem(). Converts value to JSON. */ export function setSession(key, value) { - sessionStorage.setItem(key, JSON.stringify(value)); + sessionStorage.setItem(key, JSON.stringify(value)); } diff --git a/ui/package-lock.json b/ui/package-lock.json index cb870b578..890228ef8 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "lbry-web-ui", - "version": "0.12.0", + "version": "0.12.2rc5", "lockfileVersion": 1, "dependencies": { "abbrev": { @@ -97,6 +97,12 @@ "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, "anymatch": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", @@ -140,9 +146,9 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" }, "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", + "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", "dev": true }, "array-union": { @@ -661,9 +667,9 @@ "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=" }, "babylon": { - "version": "6.17.3", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.3.tgz", - "integrity": "sha512-mq0x3HCAGGmQyZXviOVe5TRsw37Ijy3D43jCqt/9WVf+onx2dUgW3PosnqCbScAFhRO9DGs8nxoMzU0iiosMqQ==" + "version": "6.17.4", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "integrity": "sha512-kChlV+0SXkjE0vUn9OZ7pBMWRFd8uq3mZe8x1K6jhuNcAFAtEnjchFAqB+dYEXKyd+JpT6eppRR78QAr5gTsUw==" }, "balanced-match": { "version": "1.0.0", @@ -715,9 +721,15 @@ "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=" }, "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=", + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.7.tgz", + "integrity": "sha512-LxFiV5mefv0ley0SzqkOPR1bC4EbpPx8LkOz5vMe/Yi15t5hzwgO/G+tc7wOtL4PZTYjwHu8JnEiSLumuSjSfA==", + "dev": true + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "dev": true }, "boom": { @@ -783,6 +795,12 @@ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true }, + "buffer-indexof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.0.tgz", + "integrity": "sha1-9U9kfE9OJSKLqmVqLlfkPV8nCYI=", + "dev": true + }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -874,22 +892,30 @@ "dev": true }, "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true }, "cli-spinners": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", - "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.0.0.tgz", + "integrity": "sha1-75h+09SDkaw9q5GAtAanQhgNbmo=", "dev": true }, "cli-table": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "dev": true + "dev": true, + "dependencies": { + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + } + } }, "cli-truncate": { "version": "0.2.1", @@ -931,9 +957,9 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, "combined-stream": { @@ -1090,9 +1116,9 @@ "dev": true }, "create-react-class": { - "version": "15.5.4", - "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.5.4.tgz", - "integrity": "sha1-GIh1yxXi++TKWVtvQ+sOSh8A/lA=" + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", + "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=" }, "cross-spawn": { "version": "3.0.1", @@ -1166,6 +1192,12 @@ "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1229,6 +1261,24 @@ "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", "dev": true }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.1.1.tgz", + "integrity": "sha1-I2nUUDivBF84mOb6VoYq7T9AKWw=", + "dev": true + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true + }, "doctrine": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", @@ -1253,6 +1303,12 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "electron-rebuild": { + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-1.5.11.tgz", + "integrity": "sha1-bqZg3rVGpRbn76qBzVmF1WZPJFw=", + "dev": true + }, "elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", @@ -1418,9 +1474,9 @@ "dev": true }, "eslint-loader": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.7.1.tgz", - "integrity": "sha1-ULFY3WJy3O+5fphCVIN/gaWALOA=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.8.0.tgz", + "integrity": "sha512-+d9me9y9t+/k1pY5hsorY685H3yYoQT0t5pZT1TGB7L46VOoLv8+3uKHvkjpUx1aCTjeacbj4yz4s5/LcGolpg==", "dev": true, "dependencies": { "loader-utils": { @@ -1552,18 +1608,10 @@ "dev": true }, "esrecurse": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", - "integrity": "sha1-RxO2U2rffyrE8yfVWed1a/9kgiA=", - "dev": true, - "dependencies": { - "estraverse": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz", - "integrity": "sha1-9srKcokzqFDvkGYdDheYK6RxEaI=", - "dev": true - } - } + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "dev": true }, "estraverse": { "version": "4.2.0", @@ -1653,6 +1701,12 @@ "integrity": "sha1-urZdDwOqgMNYQIly/HAPkWlEtmI=", "dev": true, "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, "debug": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", @@ -1813,6 +1867,18 @@ "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=" }, + "fs-extra": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", + "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", + "dev": true + }, + "fs-promise": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-2.0.3.tgz", + "integrity": "sha1-9k5PhUvPaJqovdy6JokW2z20aFQ=", + "dev": true + }, "fs-readdir-recursive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz", @@ -2427,6 +2493,12 @@ } } }, + "gettext-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.2.2.tgz", + "integrity": "sha1-HvDadcHnWa4wicc++k0Z5AKYdI4=", + "dev": true + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -2519,9 +2591,9 @@ "dev": true }, "hash.js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz", - "integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.1.tgz", + "integrity": "sha512-I2TYCUjYQMmqmRMCp6jKMC5bvdXxGIZ/heITRR/0F1u0OP920ImEj/cXt3WgcTKBnNYGn7enxUzdai3db829JA==", "dev": true }, "hawk": { @@ -2630,6 +2702,12 @@ } } }, + "i18n-extract": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/i18n-extract/-/i18n-extract-0.4.4.tgz", + "integrity": "sha1-/VyDA4mv91WNrLh1bpPv1lNIHV8=", + "dev": true + }, "iconv-lite": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", @@ -2693,6 +2771,32 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true, + "dependencies": { + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true + } + } + }, + "internal-ip": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", + "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", "dev": true }, "interpret": { @@ -2711,6 +2815,12 @@ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, "ipaddr.js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz", @@ -2976,6 +3086,12 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true + }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -3017,6 +3133,18 @@ "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", "dev": true }, + "keytar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-4.0.3.tgz", + "integrity": "sha1-RaNnQsHPzQchDSbRuUKOKXh5vPw=", + "dependencies": { + "nan": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", + "integrity": "sha1-1bAWkSUzJql6K77p5hxV2NYDUeI=" + } + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -3054,7 +3182,39 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz", "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=", - "dev": true + "dev": true, + "dependencies": { + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true + }, + "cli-spinners": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", + "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", + "dev": true + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "ora": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", + "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true + } + } }, "listr-silent-renderer": { "version": "1.1.1", @@ -3080,7 +3240,27 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.0.tgz", "integrity": "sha1-RNwBuww0oDxXIVTU0Izemx3FYg8=", - "dev": true + "dev": true, + "dependencies": { + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true + } + } }, "load-json-file": { "version": "1.1.0", @@ -3262,7 +3442,27 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", - "dev": true + "dev": true, + "dependencies": { + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true + } + } }, "longest": { "version": "1.0.1", @@ -3375,6 +3575,12 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=" }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, "minimalistic-assert": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", @@ -3417,6 +3623,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "multicast-dns": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.1.1.tgz", + "integrity": "sha1-bn3oalcIcqsXBYrepxYLvsqBTd4=", + "dev": true + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, "multistream": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/multistream/-/multistream-2.1.0.tgz", @@ -3428,6 +3646,12 @@ "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", "dev": true }, + "mz": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.6.0.tgz", + "integrity": "sha1-yLhSHZWN8KTydoAl22nHGe5O8c4=", + "dev": true + }, "nan": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", @@ -3450,6 +3674,12 @@ "resolved": "https://registry.npmjs.org/next-event/-/next-event-1.0.0.tgz", "integrity": "sha1-53eKzeLlWALgrRh5w5z2917aYdg=" }, + "node-abi": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.0.3.tgz", + "integrity": "sha1-DKZ+XmZ7jhNDVJyhcVOoFdC7/ao=", + "dev": true + }, "node-emoji": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.5.1.tgz", @@ -3461,6 +3691,12 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.1.tgz", "integrity": "sha512-j8XsFGCLw79vWXkZtMSmmLaOk9z5SQ9bV/tkbZVCqvgwzrjAGq66igobLofHtF63NvMTp2WjytpsNTGKa+XRIQ==" }, + "node-forge": { + "version": "0.6.33", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.6.33.tgz", + "integrity": "sha1-RjgRh59XPUUVWtap9D3ClujoXrw=", + "dev": true + }, "node-gyp": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", @@ -3480,6 +3716,12 @@ } } }, + "node-loader": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/node-loader/-/node-loader-0.6.0.tgz", + "integrity": "sha1-x5fvUQle1YWZArFX9jhPY2HgWug=", + "dev": true + }, "node-notifier": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-4.6.1.tgz", @@ -3604,9 +3846,9 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" }, "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true }, "opn": { @@ -3636,9 +3878,9 @@ "dev": true }, "ora": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", - "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-1.3.0.tgz", + "integrity": "sha1-gAeN0rkqk0r2ajrXKluRBpTt5Ro=", "dev": true }, "original": { @@ -3881,9 +4123,9 @@ "dev": true }, "promise": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", - "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==" }, "prop-types": { "version": "15.5.10", @@ -3974,15 +4216,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", - "dev": true, - "dependencies": { - "safe-buffer": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", - "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==", - "dev": true - } - } + "dev": true }, "range-parser": { "version": "1.2.0", @@ -4001,19 +4235,24 @@ "integrity": "sha1-0xzLFJmuIjwNIdFIVlCqSg03TOA=" }, "react": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/react/-/react-15.6.0.tgz", - "integrity": "sha1-wjKZtI4w7TAlCM6J4aAskZ+Ca84=" + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react/-/react-15.6.1.tgz", + "integrity": "sha1-uqhDTsZ4C96ZfNw4C3nNM7ljk98=" }, "react-dom": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.0.tgz", - "integrity": "sha1-i8I8sMgOcGNVt2yp+M5Hz3vfttE=" + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz", + "integrity": "sha1-LLDtQZEDjlPCCes6eaI+Kkz5lHA=" + }, + "react-dom-factories": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/react-dom-factories/-/react-dom-factories-1.0.0.tgz", + "integrity": "sha1-9DwF5QUbME8zJRYY1byFmynka20=" }, "react-modal": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-1.9.4.tgz", - "integrity": "sha512-ourtHswaEA/KyeI20AsCOSWTC23WF3z0b1C3qzQtA5UhBOdwQ2sEK2OPJGN8u7iFGJ83etmk605aeF/i4tGVEQ==" + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-1.9.7.tgz", + "integrity": "sha512-oZNqI0ZnPD7NnfObrCMz2hxHTAw5oEuhZJ+gnyFNIQB2rR8h1YbLQTfhms1mtSJigb0J23OOWElHjXYYaKO+wg==" }, "react-redux": { "version": "5.0.5", @@ -4031,9 +4270,9 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=" }, "readable-stream": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.11.tgz", - "integrity": "sha512-h+8+r3MKEhkiVrwdKL8aWs1oc1VvBu33ueshOvS26RsZQ3Amhx/oO3TKe4lApSV9ueY6as8EAh7mtuFjdlhg9Q==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.0.tgz", + "integrity": "sha512-c7KMXGd4b48nN3OJ1U9qOsn6pXNzf6kLd3kdZCkg2sxAcoiufInqF0XckwEnlrcwuaYwonlNK8GQUIOC/WC7sg==" }, "readdirp": { "version": "2.1.0", @@ -4084,9 +4323,9 @@ } }, "redux": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-3.6.0.tgz", - "integrity": "sha1-iHwrPQub2G7KK+cFccJ2VMGeGI0=" + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.0.tgz", + "integrity": "sha512-GHjaOkEQtQnnuLoYPFkRKHIqs1i1tdTlisu/xUHfk2juzCobSy4STxs4Lz5bPkc07Owb6BeGKx/r76c9IVTkOw==" }, "redux-action-buffer": { "version": "1.1.0", @@ -4236,9 +4475,9 @@ "dev": true }, "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true }, "right-align": { @@ -4271,15 +4510,15 @@ "dev": true }, "rxjs": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.0.tgz", - "integrity": "sha1-p9sUqxV/nXqsalbmVeejhg05vyY=", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.1.tgz", + "integrity": "sha1-ti91fyeURdJloYpY+wpw3JDpFiY=", "dev": true }, "safe-buffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", + "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==" }, "sass-graph": { "version": "2.2.4", @@ -4304,6 +4543,12 @@ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", "dev": true }, + "selfsigned": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.9.1.tgz", + "integrity": "sha1-zdpEktcNSGVw+HxlVGAjVY4d+lo=", + "dev": true + }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -4451,6 +4696,12 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=" }, + "spawn-rx": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.11.tgz", + "integrity": "sha1-ZUUa1lZigB2up1VJgyp4LeAEjb8=", + "dev": true + }, "spdx-correct": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", @@ -4539,7 +4790,14 @@ "string_decoder": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz", - "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=" + "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=", + "dependencies": { + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" + } + } }, "string-width": { "version": "1.0.2", @@ -4631,6 +4889,18 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -4658,6 +4928,12 @@ } } }, + "thunky": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", + "integrity": "sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4=", + "dev": true + }, "timers-browserify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz", @@ -4848,9 +5124,9 @@ "dev": true }, "uuid": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, "v8flags": { "version": "2.1.1", @@ -4961,9 +5237,9 @@ "dev": true }, "webpack-dev-server": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.4.5.tgz", - "integrity": "sha1-MThM6BE2vhCAtLTN4OubkOVO5s8=", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.5.0.tgz", + "integrity": "sha1-TTanKLA7iyr6SO0wJCiEfOooQK0=", "dev": true, "dependencies": { "camelcase": { @@ -4972,6 +5248,32 @@ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", diff --git a/ui/package.json b/ui/package.json index 0eb8fa38e..0face051b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,12 +1,13 @@ { "name": "lbry-web-ui", - "version": "0.12.2rc3", + "version": "0.12.2rc5", "description": "LBRY UI", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack-dev-server --devtool eval --progress --colors --inline", "precommit": "lint-staged", - "prettier": "prettier --trailing-comma es5 --write js/**/*.{js,jsx}" + "prettier": "prettier --trailing-comma es5 --write js/**/*.{js,jsx}", + "extract-langs": "node extractLocals.js" }, "keywords": [ "lbry" @@ -15,11 +16,11 @@ "name": "LBRY Inc.", "email": "hello@lbry.io" }, - "license": "SEE LICENSE IN LICENSE.md", + "license": "MIT", "bugs": { "url": "https://github.com/lbryio/lbry-app/issues" }, - "homepage": "https://github.com/lbryio/lbry-app#readme", + "homepage": "https://github.com/lbryio/lbry-app", "dependencies": { "babel-cli": "^6.11.4", "babel-preset-es2015": "^6.13.2", @@ -27,6 +28,7 @@ "from2": "^2.3.0", "jshashes": "^1.0.6", "localforage": "^1.5.0", + "keytar": "^4.0.3", "node-sass": "^3.8.0", "rc-progress": "^2.0.6", "react": "^15.4.0", @@ -53,6 +55,7 @@ "babel-preset-es2015": "^6.18.0", "babel-preset-react": "^6.16.0", "babel-preset-stage-2": "^6.18.0", + "electron-rebuild": "^1.5.11", "eslint": "^3.10.2", "eslint-config-airbnb": "^13.0.0", "eslint-loader": "^1.6.1", @@ -60,8 +63,10 @@ "eslint-plugin-jsx-a11y": "^2.2.3", "eslint-plugin-react": "^6.7.1", "husky": "^0.13.4", + "i18n-extract": "^0.4.4", "json-loader": "^0.5.4", "lint-staged": "^3.6.0", + "node-loader": "^0.6.0", "node-sass": "^3.13.0", "prettier": "^1.4.2", "webpack": "^2.6.1", diff --git a/ui/watch.sh b/ui/watch.sh index 91893b1c6..bb0cc6d7b 100755 --- a/ui/watch.sh +++ b/ui/watch.sh @@ -7,16 +7,19 @@ set -euo pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -mkdir -p $DIR/dist/css -mkdir -p $DIR/dist/js +( + cd "$DIR" + mkdir -p $DIR/dist/css + mkdir -p $DIR/dist/js -if [ ! -d "$DIR/node_modules" ]; then - echo "Installing NPM modules" - npm install -fi + if [ ! -d "$DIR/node_modules" ]; then + echo "Installing NPM modules" + npm install + fi -# run sass once without --watch to force update. then run with --watch to keep watching -$DIR/node_modules/.bin/node-sass --output $DIR/../app/dist/css --sourcemap=none $DIR/scss/ -$DIR/node_modules/.bin/node-sass --output $DIR/../app/dist/css --sourcemap=none --watch $DIR/scss/ & + # run sass once without --watch to force update. then run with --watch to keep watching + node_modules/.bin/node-sass --output $DIR/../app/dist/css --sourcemap=none $DIR/scss/ + node_modules/.bin/node-sass --output $DIR/../app/dist/css --sourcemap=none --watch $DIR/scss/ & -node_modules/.bin/webpack --config webpack.dev.config.js --progress --colors --watch + node_modules/.bin/webpack --config webpack.dev.config.js --progress --colors --watch +) \ No newline at end of file diff --git a/ui/webpack.config.js b/ui/webpack.config.js index b0ab6f08a..fd890022e 100644 --- a/ui/webpack.config.js +++ b/ui/webpack.config.js @@ -33,6 +33,10 @@ module.exports = { // define an include so we check just the files we need include: PATHS.app }, + { + test: /\.node$/, + use: ["node-loader"] + }, { test: /\.css$/, use: ["style-loader", "css-loader"] diff --git a/ui/webpack.dev.config.js b/ui/webpack.dev.config.js index 42107a3c5..604aa14d4 100644 --- a/ui/webpack.dev.config.js +++ b/ui/webpack.dev.config.js @@ -41,6 +41,10 @@ module.exports = { // define an include so we check just the files we need include: PATHS.app }, + { + test: /\.node$/, + use: ["node-loader"] + }, { test: /\.css$/, use: ["style-loader", "css-loader"]