diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a6a82780..3269d2934 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ Web UI version numbers should always match the corresponding version of LBRY App * Fixed inappropriate text showing on searches * Stop discover page from pushing jumping vertically while loading * Restored feedback on claim amounts + * Fixed hiding price input when Free is checked on publish form + * Fixed hiding new identity fields on publish form ### Deprecated * diff --git a/app/main.js b/app/main.js index 35a50f224..69fad7d92 100644 --- a/app/main.js +++ b/app/main.js @@ -27,7 +27,7 @@ const {version: localVersion} = require(app.getAppPath() + '/package.json'); const VERSION_CHECK_INTERVAL = 30 * 60 * 1000; const LATEST_RELEASE_API_URL = 'https://api.github.com/repos/lbryio/lbry-app/releases/latest'; - +const DAEMON_PATH = process.env.LBRY_DAEMON || path.join(__dirname, 'dist', 'lbrynet-daemon'); let client = jayson.client.http({ host: 'localhost', @@ -207,13 +207,8 @@ function handleDaemonSubprocessExited() { function launchDaemon() { assert(!daemonSubprocess, 'Tried to launch daemon twice'); - if (process.env.LBRY_DAEMON) { - executable = process.env.LBRY_DAEMON; - } else { - executable = path.join(__dirname, 'dist', 'lbrynet-daemon'); - } - console.log('Launching daemon:', executable) - daemonSubprocess = child_process.spawn(executable) + console.log('Launching daemon:', DAEMON_PATH) + daemonSubprocess = child_process.spawn(DAEMON_PATH) // Need to handle the data event instead of attaching to // process.stdout because the latter doesn't work. I believe on // windows it buffers stdout and we don't get any meaningful output diff --git a/app/package.json b/app/package.json index aee4e6085..ced806d22 100644 --- a/app/package.json +++ b/app/package.json @@ -18,5 +18,8 @@ }, "devDependencies": { "electron-rebuild": "^1.5.11" + }, + "lbrySettings": { + "lbrynetDaemonVersion": "0.14.1" } } diff --git a/build/DAEMON_URL b/build/DAEMON_URL deleted file mode 100644 index e7d65abfa..000000000 --- a/build/DAEMON_URL +++ /dev/null @@ -1 +0,0 @@ -https://github.com/lbryio/lbry/releases/download/v0.14.1/lbrynet-daemon-v0.14.1-OSNAME.zip diff --git a/build/build.sh b/build/build.sh index c5ccba34f..9fa139877 100755 --- a/build/build.sh +++ b/build/build.sh @@ -79,11 +79,14 @@ if $OSX; then else OSNAME="linux" fi -DAEMON_URL="$(cat "$BUILD_DIR/DAEMON_URL" | sed "s/OSNAME/${OSNAME}/")" +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" + + ################### # Build the app # ################### diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js index f50ea9467..c3f23a3a5 100644 --- a/ui/js/actions/app.js +++ b/ui/js/actions/app.js @@ -16,9 +16,9 @@ import { doFileList } from "actions/file_info"; const { remote, ipcRenderer, shell } = require("electron"); const path = require("path"); -const app = require("electron").remote.app; const { download } = remote.require("electron-dl"); const fs = remote.require("fs"); +const { lbrySettings: config } = require("../../../app/package.json"); const queryStringFromParams = params => { return Object.keys(params).map(key => `${key}=${params[key]}`).join("&"); @@ -33,7 +33,7 @@ export function doNavigate(path, params = {}) { const state = getState(); const pageTitle = selectPageTitle(state); - dispatch(doHistoryPush(params, pageTitle, url)); + dispatch(doHistoryPush({ params }, pageTitle, url)); }; } @@ -51,7 +51,7 @@ export function doAuthNavigate(pathAfterAuth = null, params = {}) { }; } -export function doChangePath(path) { +export function doChangePath(path, options = {}) { return function(dispatch, getState) { dispatch({ type: types.CHANGE_PATH, @@ -62,8 +62,12 @@ export function doChangePath(path) { const state = getState(); const pageTitle = selectPageTitle(state); + const scrollY = options.scrollY; + window.document.title = pageTitle; - window.scrollTo(0, 0); + + if (scrollY) window.scrollTo(0, scrollY); + else window.scrollTo(0, 0); const currentPage = selectCurrentPage(state); if (currentPage === "search") { @@ -81,10 +85,26 @@ export function doHistoryBack() { }; } -export function doHistoryPush(params, title, relativeUrl) { +export function doHistoryPush(currentState, title, relativeUrl) { return function(dispatch, getState) { title += " - LBRY"; - history.pushState(params, title, `#${relativeUrl}`); + history.pushState(currentState, title, `#${relativeUrl}`); + }; +} + +export function doRecordScroll(scroll) { + return function(dispatch, getState) { + const state = getState(); + const historyState = history.state; + + if (!historyState) return; + + historyState.scrollY = scroll; + history.replaceState( + historyState, + document.title, + `#${state.app.currentPath}` + ); }; } @@ -131,8 +151,9 @@ export function doDownloadUpgrade() { return function(dispatch, getState) { const state = getState(); // Make a new directory within temp directory so the filename is guaranteed to be available - const dir = fs.mkdtempSync(app.getPath("temp") + require("path").sep); - const upgradeFilename = selectUpgradeFilename(state); + const dir = fs.mkdtempSync( + remote.app.getPath("temp") + require("path").sep + ); let options = { onProgress: p => dispatch(doUpdateDownloadProgress(Math.round(p * 100))), @@ -216,6 +237,18 @@ export function doCheckUpgradeAvailable() { }; } +export function doCheckDaemonVersion() { + return function(dispatch, getState) { + lbry.version().then(({ lbrynet_version }) => { + dispatch({ + type: config.lbrynetDaemonVersion == lbrynet_version + ? types.DAEMON_VERSION_MATCH + : types.DAEMON_VERSION_MISMATCH, + }); + }); + }; +} + export function doAlertError(errorList) { return function(dispatch, getState) { const state = getState(); @@ -232,7 +265,8 @@ export function doAlertError(errorList) { } export function doDaemonReady() { - return function(dispatch) { + return function(dispatch, getState) { + history.replaceState({}, document.title, `#/discover`); dispatch(doAuthenticate()); dispatch({ type: types.DAEMON_READY, @@ -262,3 +296,10 @@ export function doClearCache() { return Promise.resolve(); }; } + +export function doQuitAndLaunchDaemonHelp() { + return function(dispatch, getState) { + shell.openExternal("https://lbry.io/faq/incompatible-protocol-version"); + remote.app.quit(); + }; +} diff --git a/ui/js/actions/user.js b/ui/js/actions/user.js index 0ca9bc8ee..799bc8cbc 100644 --- a/ui/js/actions/user.js +++ b/ui/js/actions/user.js @@ -159,22 +159,22 @@ export function doUserIdentityVerify(stripeToken) { } }) .catch(error => { - let user = selectUser(getState()); - user.is_identity_verified = true; - if (user.is_identity_verified) { - dispatch({ - type: types.USER_IDENTITY_VERIFY_SUCCESS, - data: { user }, - }); - } else { - throw new Error( - "Your identity is still not verified. This should not happen." - ); //shouldn't happen - } - // dispatch({ - // type: types.USER_IDENTITY_VERIFY_FAILURE, - // data: { error: error.toString() }, - // }); + // let user = selectUser(getState()); + // user.is_identity_verified = true; + // if (user.is_identity_verified) { + // dispatch({ + // type: types.USER_IDENTITY_VERIFY_SUCCESS, + // data: { user }, + // }); + // } else { + // throw new Error( + // "Your identity is still not verified. This should not happen." + // ); //shouldn't happen + // } + dispatch({ + type: types.USER_IDENTITY_VERIFY_FAILURE, + data: { error: error.toString() }, + }); }); }; } diff --git a/ui/js/app.js b/ui/js/app.js index 41701ee18..c5bfdcfbd 100644 --- a/ui/js/app.js +++ b/ui/js/app.js @@ -2,7 +2,9 @@ import store from "store.js"; import lbry from "./lbry.js"; const env = ENV; -const config = require(`./config/${env}`); +const config = { + ...require(`./config/${env}`), +}; const language = lbry.getClientSetting("language") ? lbry.getClientSetting("language") : "en"; diff --git a/ui/js/component/app/index.js b/ui/js/component/app/index.js index 0d4e1c093..ff4940f88 100644 --- a/ui/js/component/app/index.js +++ b/ui/js/component/app/index.js @@ -5,6 +5,7 @@ import { doCheckUpgradeAvailable, doOpenModal, doAlertError, + doRecordScroll, } from "actions/app"; import { doUpdateBalance } from "actions/wallet"; import { selectWelcomeModalAcknowledged } from "selectors/app"; @@ -36,6 +37,7 @@ const perform = dispatch => ({ checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()), openWelcomeModal: () => dispatch(doOpenModal(modals.WELCOME)), updateBalance: balance => dispatch(doUpdateBalance(balance)), + recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)), }); export default connect(select, perform)(App); diff --git a/ui/js/component/app/view.jsx b/ui/js/component/app/view.jsx index 7c6b17eca..c4b857a8b 100644 --- a/ui/js/component/app/view.jsx +++ b/ui/js/component/app/view.jsx @@ -26,6 +26,10 @@ class App extends React.PureComponent { }); this.showWelcome(this.props); + + this.scrollListener = () => this.props.recordScroll(window.scrollY); + + window.addEventListener("scroll", this.scrollListener); } componentWillReceiveProps(nextProps) { @@ -50,6 +54,10 @@ class App extends React.PureComponent { } } + componentWillUnmount() { + window.removeEventListener("scroll", this.scrollListener); + } + render() { const { modal } = this.props; diff --git a/ui/js/component/fileCard/view.jsx b/ui/js/component/fileCard/view.jsx index 5a35d740d..cb57a4308 100644 --- a/ui/js/component/fileCard/view.jsx +++ b/ui/js/component/fileCard/view.jsx @@ -78,10 +78,11 @@ class FileCard extends React.PureComponent { onClick={() => navigate("/show", { uri })} className="card__link" > +
-
+
{title} -
+
@@ -92,7 +93,6 @@ class FileCard extends React.PureComponent {
-
{description}
diff --git a/ui/js/component/link/view.jsx b/ui/js/component/link/view.jsx index 2f9c7478e..a39e9947c 100644 --- a/ui/js/component/link/view.jsx +++ b/ui/js/component/link/view.jsx @@ -11,7 +11,6 @@ const Link = props => { icon, badge, button, - hidden, disabled, children, } = props; diff --git a/ui/js/component/modal-page.js b/ui/js/component/modal-page.js deleted file mode 100644 index f63d120f5..000000000 --- a/ui/js/component/modal-page.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import ReactModal from "react-modal"; - -export class ModalPage extends React.PureComponent { - render() { - return ( - -
- {this.props.children} -
-
- ); - } -} - -export default ModalPage; diff --git a/ui/js/component/modalIncompatibleDaemon/index.jsx b/ui/js/component/modalIncompatibleDaemon/index.jsx new file mode 100644 index 000000000..27ddecd8d --- /dev/null +++ b/ui/js/component/modalIncompatibleDaemon/index.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import { connect } from "react-redux"; +import { doQuit, doSkipWrongDaemonNotice } from "actions/app"; +import { doQuitAndLaunchDaemonHelp } from "actions/app"; +import ModalIncompatibleDaemon from "./view"; + +const select = state => ({}); + +const perform = dispatch => ({ + quitAndLaunchDaemonHelp: () => dispatch(doQuitAndLaunchDaemonHelp()), +}); + +export default connect(select, perform)(ModalIncompatibleDaemon); diff --git a/ui/js/component/modalIncompatibleDaemon/view.jsx b/ui/js/component/modalIncompatibleDaemon/view.jsx new file mode 100644 index 000000000..851a2ec88 --- /dev/null +++ b/ui/js/component/modalIncompatibleDaemon/view.jsx @@ -0,0 +1,24 @@ +import React from "react"; +import { Modal } from "component/modal"; + +class ModalIncompatibleDaemon extends React.PureComponent { + render() { + const { quitAndLaunchDaemonHelp } = this.props; + + return ( + + {__( + "This browser is running with an incompatible version of the LBRY protocol and your install must be repaired." + )} + + ); + } +} + +export default ModalIncompatibleDaemon; diff --git a/ui/js/component/modalWelcome/index.js b/ui/js/component/modalWelcome/index.js index b41d17013..00a50c86b 100644 --- a/ui/js/component/modalWelcome/index.js +++ b/ui/js/component/modalWelcome/index.js @@ -1,7 +1,7 @@ import React from "react"; import rewards from "rewards"; import { connect } from "react-redux"; -import { doCloseModal, doNavigate } from "actions/app"; +import { doCloseModal, doAuthNavigate } from "actions/app"; import { doSetClientSetting } from "actions/settings"; import { selectUserIsRewardApproved } from "selectors/user"; import { @@ -30,7 +30,7 @@ const perform = dispatch => () => { return { verifyAccount: () => { closeModal(); - dispatch(doNavigate("/auth")); + dispatch(doAuthNavigate("/rewards")); }, closeModal: closeModal, }; diff --git a/ui/js/component/publishForm/internal/channelSection.jsx b/ui/js/component/publishForm/internal/channelSection.jsx index 6c7802625..4e598a9de 100644 --- a/ui/js/component/publishForm/internal/channelSection.jsx +++ b/ui/js/component/publishForm/internal/channelSection.jsx @@ -93,6 +93,7 @@ class ChannelSection extends React.PureComponent { "This LBC remains yours and the deposit can be undone at any time." ); + const channel = this.state.addingChannel ? "new" : this.props.channel; const { fetchingChannels, channels = [] } = this.props; let channelContent = []; @@ -102,7 +103,7 @@ class ChannelSection extends React.PureComponent { type="select" tabIndex="1" onChange={this.handleChannelChange.bind(this)} - value={this.props.channel} + value={channel} > )} ); @@ -124,9 +125,10 @@ class ChannelSection extends React.PureComponent { return (
-

{__("Identity")}

+

{__("Channel Name")}

- {__("Who created this content?")} + {__("This is the channel that broadcasts your content.")} + {__("Ex. @Marvel, @TheBeatles, @BooksByJoe")}
@@ -137,9 +139,7 @@ class ChannelSection extends React.PureComponent { { - this.handleNewChannelNameChange(event); - }} + onChange={this.handleNewChannelNameChange.bind(this)} value={this.state.newChannelName} /> { - this.handleFeePrefChange(false); - }} - defaultChecked={!this.state.isFee} + onChange={() => this.handleFeePrefChange(false)} + checked={!this.state.isFee} /> { this.handleFeePrefChange(true); }} - defaultChecked={this.state.isFee} + checked={this.state.isFee} />
-

{__("Address")}

+

{__("Content URL")}

- {__("Where should this content permanently reside?")} + {__( + "This is the exact address where people find your content (ex. lbry://myvideo)." + )} {" "} .
diff --git a/ui/js/component/splash/index.js b/ui/js/component/splash/index.js new file mode 100644 index 000000000..1a87f476b --- /dev/null +++ b/ui/js/component/splash/index.js @@ -0,0 +1,19 @@ +import React from "react"; +import { connect } from "react-redux"; + +import { selectCurrentModal, selectDaemonVersionMatched } from "selectors/app"; +import { doCheckDaemonVersion } from "actions/app"; +import SplashScreen from "./view"; + +const select = state => { + return { + modal: selectCurrentModal(state), + daemonVersionMatched: selectDaemonVersionMatched(state), + }; +}; + +const perform = dispatch => ({ + checkDaemonVersion: () => dispatch(doCheckDaemonVersion()), +}); + +export default connect(select, perform)(SplashScreen); diff --git a/ui/js/component/splash.js b/ui/js/component/splash/view.jsx similarity index 59% rename from ui/js/component/splash.js rename to ui/js/component/splash/view.jsx index eb67340e1..7cccd5bf0 100644 --- a/ui/js/component/splash.js +++ b/ui/js/component/splash/view.jsx @@ -1,6 +1,9 @@ import React from "react"; -import lbry from "../lbry.js"; -import LoadScreen from "./load_screen.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"; export class SplashScreen extends React.PureComponent { static propTypes = { @@ -14,6 +17,7 @@ export class SplashScreen extends React.PureComponent { this.state = { details: __("Starting daemon"), message: __("Connecting"), + isRunning: false, isLagging: false, }; } @@ -35,10 +39,16 @@ export class SplashScreen extends React.PureComponent { message: __("Testing Network"), details: __("Waiting for name resolution"), isLagging: false, + isRunning: true, }); lbry.resolve({ uri: "lbry://one" }).then(() => { - this.props.onLoadDone(); + // Only leave the load screen if the daemon version matched; + // otherwise we'll notify the user at the end of the load screen. + + if (this.props.daemonVersionMatched) { + this.props.onReadyToLaunch(); + } }); return; } @@ -54,6 +64,7 @@ export class SplashScreen extends React.PureComponent { componentDidMount() { lbry .connect() + .then(this.props.checkDaemonVersion) .then(() => { this.updateStatus(); }) @@ -69,12 +80,24 @@ export class SplashScreen extends React.PureComponent { } render() { + const { modal } = this.props; + return ( - +
+ + {/* Temp hack: don't show any modals on splash screen daemon is running; + daemon doesn't let you quit during startup, so the "Quit" buttons + in the modals won't work. */} + {modal == "incompatibleDaemon" && + this.state.isRunning && + } + {modal == "upgrade" && this.state.isRunning && } + {modal == "downloading" && this.state.isRunning && } +
); } } diff --git a/ui/js/component/userVerify/view.jsx b/ui/js/component/userVerify/view.jsx index 1596b3726..705ee424c 100644 --- a/ui/js/component/userVerify/view.jsx +++ b/ui/js/component/userVerify/view.jsx @@ -25,15 +25,13 @@ class UserVerify extends React.PureComponent { const { errorMessage, isPending, reward } = this.props; return (
-

- + {(!reward || !reward.transaction_id) && +

Please link a credit card to confirm your identity and receive{" "} - - {reward - ? - : your reward} - {"."} -

+ {reward + ? + : your reward} +

}

{__("This is to prevent abuse. You will not be charged.")}

{errorMessage &&

{errorMessage}

} { if (hash !== "") { const url = hash.split("#")[1]; - const params = event.state; + const { params, scrollY } = event.state || {}; const queryString = toQueryString(params); - app.store.dispatch(doChangePath(`${url}?${queryString}`)); + app.store.dispatch(doChangePath(`${url}?${queryString}`, { scrollY })); } else { app.store.dispatch(doChangePath("/discover")); } @@ -111,7 +111,12 @@ var init = function() { if (window.sessionStorage.getItem("loaded") == "y") { onDaemonReady(); } else { - ReactDOM.render(, canvas); + ReactDOM.render( + + + , + canvas + ); } }; diff --git a/ui/js/page/filePage/index.js b/ui/js/page/filePage/index.js index f6629214c..ec151ab50 100644 --- a/ui/js/page/filePage/index.js +++ b/ui/js/page/filePage/index.js @@ -25,7 +25,7 @@ const makeSelect = () => { contentType: selectContentType(state, props), costInfo: selectCostInfo(state, props), metadata: selectMetadata(state, props), - showNsfw: !selectShowNsfw(state), + obscureNsfw: !selectShowNsfw(state), fileInfo: selectFileInfo(state, props), }); diff --git a/ui/js/page/rewards/index.js b/ui/js/page/rewards/index.js index 52e18250a..ce24ce6e7 100644 --- a/ui/js/page/rewards/index.js +++ b/ui/js/page/rewards/index.js @@ -6,11 +6,11 @@ import { selectRewards, } from "selectors/rewards"; import { - selectUserIsRewardEligible, + selectUser, selectUserHasEmail, selectUserIsVerificationCandidate, } from "selectors/user"; -import { doAuthNavigate } from "actions/app"; +import { doAuthNavigate, doNavigate } from "actions/app"; import { doRewardList } from "actions/rewards"; import rewards from "rewards"; import RewardsPage from "./view"; @@ -21,15 +21,14 @@ const select = (state, props) => { return { fetching: selectFetchingRewards(state), rewards: selectRewards(state), - hasEmail: selectUserHasEmail(state), - isEligible: selectUserIsRewardEligible(state), - isVerificationCandidate: selectUserIsVerificationCandidate(state), newUserReward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }), + user: selectUser(state), }; }; const perform = dispatch => ({ fetchRewards: () => dispatch(doRewardList()), + navigate: path => dispatch(doNavigate(path)), doAuth: () => { dispatch(doAuthNavigate("/rewards")); }, diff --git a/ui/js/page/rewards/view.jsx b/ui/js/page/rewards/view.jsx index 842f90685..e590d5895 100644 --- a/ui/js/page/rewards/view.jsx +++ b/ui/js/page/rewards/view.jsx @@ -45,39 +45,11 @@ class RewardsPage extends React.PureComponent { } render() { - const { - doAuth, - fetching, - isEligible, - isVerificationCandidate, - hasEmail, - rewards, - newUserReward, - } = this.props; + const { doAuth, fetching, navigate, rewards, user } = this.props; - let content, - isCard = false; + let content, cardHeader; - if (!hasEmail || isVerificationCandidate) { - content = ( -
-
-

{__("Only verified accounts are eligible to earn rewards.")}

-
-
- -
-
- ); - isCard = true; - } else if (!isEligible) { - isCard = true; - content = ( -
-

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

-
- ); - } else if (fetching) { + if (fetching) { content = (
@@ -99,10 +71,54 @@ class RewardsPage extends React.PureComponent { ); } + if ( + user && + (!user.has_email || + !user.has_verified_email || + !user.is_identity_verified) + ) { + cardHeader = ( +
+
+

{__("Only verified accounts are eligible to earn rewards.")}

+
+
+ +
+
+ ); + } else if (user && !user.is_reward_approved) { + cardHeader = ( +
+

+ {__( + "This account must undergo review before you can participate in the rewards program." + )} + {" "} + {__("This can take anywhere from several minutes to several days.")} +

+

+ {__("You will receive an email when this process is complete.")} +

+

+ {__("Please enjoy free content in the meantime!")} +

+

+ navigate("/discover")} + button="primary" + label="Return Home" + /> +

+
+ ); + } + return (
- {isCard ?
{content}
: content} + {cardHeader &&
{cardHeader}
} + {content}
); } diff --git a/ui/js/reducers/app.js b/ui/js/reducers/app.js index bc24ac85e..6ee007b86 100644 --- a/ui/js/reducers/app.js +++ b/ui/js/reducers/app.js @@ -1,4 +1,5 @@ import * as types from "constants/action_types"; +import * as modalTypes from "constants/modal_types"; import lbry from "lbry"; const currentPath = () => { @@ -18,6 +19,7 @@ const defaultState = { pathAfterAuth: "/discover", platform: process.platform, upgradeSkipped: sessionStorage.getItem("upgradeSkipped"), + daemonVersionMatched: null, daemonReady: false, hasSignature: false, badgeNumber: 0, @@ -29,6 +31,19 @@ reducers[types.DAEMON_READY] = function(state, action) { }); }; +reducers[types.DAEMON_VERSION_MATCH] = function(state, action) { + return Object.assign({}, state, { + daemonVersionMatched: true, + }); +}; + +reducers[types.DAEMON_VERSION_MISMATCH] = function(state, action) { + return Object.assign({}, state, { + daemonVersionMatched: false, + modal: modalTypes.INCOMPATIBLE_DAEMON, + }); +}; + reducers[types.CHANGE_PATH] = function(state, action) { return Object.assign({}, state, { currentPath: action.data.path, diff --git a/ui/js/selectors/app.js b/ui/js/selectors/app.js index b70fbbdce..3951a8216 100644 --- a/ui/js/selectors/app.js +++ b/ui/js/selectors/app.js @@ -177,6 +177,11 @@ export const selectDaemonReady = createSelector( state => state.daemonReady ); +export const selectDaemonVersionMatched = createSelector( + _selectState, + state => state.daemonVersionMatched +); + export const selectSnackBar = createSelector( _selectState, state => state.snackBar || {} diff --git a/ui/js/selectors/user.js b/ui/js/selectors/user.js index a4f266ff0..6ade78efe 100644 --- a/ui/js/selectors/user.js +++ b/ui/js/selectors/user.js @@ -30,11 +30,6 @@ export const selectUserHasEmail = createSelector( (user, email) => (user && user.has_email) || !!email ); -export const selectUserIsRewardEligible = createSelector( - selectUser, - user => user && user.is_reward_eligible -); - export const selectUserIsRewardApproved = createSelector( selectUser, user => user && user.is_reward_approved diff --git a/ui/js/utils.js b/ui/js/utils.js index b41a2e0d4..18e4ca21e 100644 --- a/ui/js/utils.js +++ b/ui/js/utils.js @@ -1,3 +1,5 @@ +const { remote } = require("electron"); + /** * Thin wrapper around localStorage.getItem(). Parses JSON and returns undefined if the value * is not set yet. diff --git a/ui/scss/all.scss b/ui/scss/all.scss index 6b421196a..61612d7df 100644 --- a/ui/scss/all.scss +++ b/ui/scss/all.scss @@ -15,7 +15,6 @@ @import "component/_channel-indicator.scss"; @import "component/_notice.scss"; @import "component/_modal.scss"; -@import "component/_modal-page.scss"; @import "component/_snack-bar.scss"; @import "component/_video.scss"; @import "page/_developer.scss"; diff --git a/ui/scss/component/_card.scss b/ui/scss/component/_card.scss index e5126d6e7..d8416d226 100644 --- a/ui/scss/component/_card.scss +++ b/ui/scss/component/_card.scss @@ -19,18 +19,29 @@ $padding-card-horizontal: $spacing-vertical * 2/3; .card--obscured .card__inner { filter: blur($blur-intensity-nsfw); } -.card__title-primary { +.card__title-primary, +.card__title-identity, +.card__actions, +.card__content, +.card__subtext { padding: 0 $padding-card-horizontal; +} +.card--small { + .card__title-primary, + .card__title-identity, + .card__actions, + .card__content, + .card__subtext { + padding: 0 $padding-card-horizontal / 2; + } +} +.card__title-primary { margin-top: $spacing-vertical * 2/3; } .card__title-identity { - padding: 0 $padding-card-horizontal; margin-top: $spacing-vertical * 1/3; margin-bottom: $spacing-vertical * 1/3; } -.card__actions { - padding: 0 $padding-card-horizontal; -} .card__actions { margin-top: $spacing-vertical * 2/3; } @@ -45,21 +56,18 @@ $padding-card-horizontal: $spacing-vertical * 2/3; .card__content { margin-top: $spacing-vertical * 2/3; margin-bottom: $spacing-vertical * 2/3; - padding: 0 $padding-card-horizontal; } .card__subtext { - color: #444; - margin-top: 12px; - font-size: 0.9em; - margin-top: $spacing-vertical * 2/3; - margin-bottom: $spacing-vertical * 2/3; - padding: 0 $padding-card-horizontal; + color: $color-meta-light; + font-size: 0.82em; + margin-top: $spacing-vertical * 1/3; + margin-bottom: $spacing-vertical * 1/3; } .card__subtext--allow-newlines { white-space: pre-wrap; } .card__subtext--two-lines { - height: $font-size * 0.9 * $font-line-height * 2; + height: $font-size * 0.82 * $font-line-height * 2; /*this is so one line text still has the proper height*/ } .card-overlay { position: absolute; @@ -144,7 +152,7 @@ $card-link-scaling: 1.1; top: 36% } -$width-card-small: $spacing-vertical * 12; +$width-card-small: $spacing-vertical * 10; $height-card-small: $spacing-vertical * 15; .card--small { diff --git a/ui/scss/component/_modal-page.scss b/ui/scss/component/_modal-page.scss deleted file mode 100644 index ada366f61..000000000 --- a/ui/scss/component/_modal-page.scss +++ /dev/null @@ -1,54 +0,0 @@ -@import "../global"; - -.modal-page { - position: fixed; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - border: 1px solid rgb(204, 204, 204); - background: rgb(255, 255, 255); - overflow: auto; -} - -.modal-page--full { - left: 0; - right: 0; - top: 0; - bottom: 0; - .modal-page__content { - max-width: 500px; - } -} - -/* -.modal-page { - position: fixed; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - border: 1px solid rgb(204, 204, 204); - background: rgb(255, 255, 255); - overflow: auto; - border-radius: 4px; - outline: none; - padding: 36px; - - top: 25px; - left: 25px; - right: 25px; - bottom: 25px; -} -*/ - -.modal-page__content { - h1, h2 { - margin-bottom: $spacing-vertical / 2; - } - h3, h4 { - margin-bottom: $spacing-vertical / 4; - } -} \ No newline at end of file