diff --git a/CHANGELOG.md b/CHANGELOG.md index c3e07a4f9..aae0813e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,22 +10,25 @@ Web UI version numbers should always match the corresponding version of LBRY App ### Added * Added a new component, `FormFieldPrice` which is now used in Publish and Settings * Added a theme system to select different themes in Settings. - * New Dark theme. - * Added wallet backup guide reference - * - + * Added a new dark theme. + * 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 * 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. - * Some form field refactoring as we progress towards form sanity. + * 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. - * Removed confusing placeholder text from email input - * Updated several packages and fixed warnings in build process (all but the [fsevents warning](https://github.com/yarnpkg/yarn/issues/3738), which is rather dramatic) + * 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 - * Now using setState in formFieldPrice * Public page now properly checks for all required fields are filled * Fixed pagination styling for pages > 5 (#416) * Fixed sizing on squat videos (#419) diff --git a/README.md b/README.md index e446b747a..5e94a8bf5 100644 --- a/README.md +++ b/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. \ No newline at end of file +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. diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js index 44964b0ba..e70e9767f 100644 --- a/ui/js/actions/app.js +++ b/ui/js/actions/app.js @@ -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()); diff --git a/ui/js/app.js b/ui/js/app.js index c5bfdcfbd..2504b72e8 100644 --- a/ui/js/app.js +++ b/ui/js/app.js @@ -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", diff --git a/ui/js/component/app/view.jsx b/ui/js/component/app/view.jsx index e8ebbcd09..32cb74247 100644 --- a/ui/js/component/app/view.jsx +++ b/ui/js/component/app/view.jsx @@ -1,15 +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 ModalRouter from "modal/modalRouter"; import lbry from "lbry"; -import * as modals from "constants/modal_types"; class App extends React.PureComponent { componentWillMount() { @@ -34,50 +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 (
- {modal == modals.UPGRADE && } - {modal == modals.DOWNLOADING && } - {modal == modals.ERROR && } - {modal == modals.INSUFFICIENT_CREDITS && } - {modal == modals.WELCOME && } - {modal == modals.FIRST_REWARD && } - {modal == modals.AUTHENTICATION_FAILURE && } +
); } diff --git a/ui/js/component/fileActions/view.jsx b/ui/js/component/fileActions/view.jsx index df624a345..7b3463d3d 100644 --- a/ui/js/component/fileActions/view.jsx +++ b/ui/js/component/fileActions/view.jsx @@ -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 { diff --git a/ui/js/component/header/index.js b/ui/js/component/header/index.js index eda8923d3..8c8c1f96a 100644 --- a/ui/js/component/header/index.js +++ b/ui/js/component/header/index.js @@ -1,11 +1,14 @@ import React from "react"; import { formatCredits } from "utils"; 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); diff --git a/ui/js/component/header/view.jsx b/ui/js/component/header/view.jsx index 76758b210..81b70d703 100644 --- a/ui/js/component/header/view.jsx +++ b/ui/js/component/header/view.jsx @@ -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 (