Add identity verification to app #366
35 changed files with 317 additions and 213 deletions
|
@ -25,6 +25,8 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
* Fixed inappropriate text showing on searches
|
* Fixed inappropriate text showing on searches
|
||||||
* Stop discover page from pushing jumping vertically while loading
|
* Stop discover page from pushing jumping vertically while loading
|
||||||
* Restored feedback on claim amounts
|
* 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
|
### Deprecated
|
||||||
*
|
*
|
||||||
|
|
11
app/main.js
11
app/main.js
|
@ -27,7 +27,7 @@ const {version: localVersion} = require(app.getAppPath() + '/package.json');
|
||||||
|
|
||||||
const VERSION_CHECK_INTERVAL = 30 * 60 * 1000;
|
const VERSION_CHECK_INTERVAL = 30 * 60 * 1000;
|
||||||
const LATEST_RELEASE_API_URL = 'https://api.github.com/repos/lbryio/lbry-app/releases/latest';
|
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({
|
let client = jayson.client.http({
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
|
@ -207,13 +207,8 @@ function handleDaemonSubprocessExited() {
|
||||||
function launchDaemon() {
|
function launchDaemon() {
|
||||||
assert(!daemonSubprocess, 'Tried to launch daemon twice');
|
assert(!daemonSubprocess, 'Tried to launch daemon twice');
|
||||||
|
|
||||||
if (process.env.LBRY_DAEMON) {
|
console.log('Launching daemon:', DAEMON_PATH)
|
||||||
executable = process.env.LBRY_DAEMON;
|
daemonSubprocess = child_process.spawn(DAEMON_PATH)
|
||||||
} else {
|
|
||||||
executable = path.join(__dirname, 'dist', 'lbrynet-daemon');
|
|
||||||
}
|
|
||||||
console.log('Launching daemon:', executable)
|
|
||||||
daemonSubprocess = child_process.spawn(executable)
|
|
||||||
// Need to handle the data event instead of attaching to
|
// Need to handle the data event instead of attaching to
|
||||||
// process.stdout because the latter doesn't work. I believe on
|
// process.stdout because the latter doesn't work. I believe on
|
||||||
// windows it buffers stdout and we don't get any meaningful output
|
// windows it buffers stdout and we don't get any meaningful output
|
||||||
|
|
|
@ -18,5 +18,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron-rebuild": "^1.5.11"
|
"electron-rebuild": "^1.5.11"
|
||||||
|
},
|
||||||
|
"lbrySettings": {
|
||||||
|
"lbrynetDaemonVersion": "0.14.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
https://github.com/lbryio/lbry/releases/download/v0.14.1/lbrynet-daemon-v0.14.1-OSNAME.zip
|
|
|
@ -79,11 +79,14 @@ if $OSX; then
|
||||||
else
|
else
|
||||||
OSNAME="linux"
|
OSNAME="linux"
|
||||||
fi
|
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"
|
wget --quiet "$DAEMON_URL" -O "$BUILD_DIR/daemon.zip"
|
||||||
unzip "$BUILD_DIR/daemon.zip" -d "$ROOT/app/dist/"
|
unzip "$BUILD_DIR/daemon.zip" -d "$ROOT/app/dist/"
|
||||||
rm "$BUILD_DIR/daemon.zip"
|
rm "$BUILD_DIR/daemon.zip"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# Build the app #
|
# Build the app #
|
||||||
###################
|
###################
|
||||||
|
|
|
@ -16,9 +16,9 @@ import { doFileList } from "actions/file_info";
|
||||||
|
|
||||||
const { remote, ipcRenderer, shell } = require("electron");
|
const { remote, ipcRenderer, shell } = require("electron");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const app = require("electron").remote.app;
|
|
||||||
const { download } = remote.require("electron-dl");
|
const { download } = remote.require("electron-dl");
|
||||||
const fs = remote.require("fs");
|
const fs = remote.require("fs");
|
||||||
|
const { lbrySettings: config } = require("../../../app/package.json");
|
||||||
|
|
||||||
const queryStringFromParams = params => {
|
const queryStringFromParams = params => {
|
||||||
return Object.keys(params).map(key => `${key}=${params[key]}`).join("&");
|
return Object.keys(params).map(key => `${key}=${params[key]}`).join("&");
|
||||||
|
@ -33,7 +33,7 @@ export function doNavigate(path, params = {}) {
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const pageTitle = selectPageTitle(state);
|
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) {
|
return function(dispatch, getState) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.CHANGE_PATH,
|
type: types.CHANGE_PATH,
|
||||||
|
@ -62,8 +62,12 @@ export function doChangePath(path) {
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const pageTitle = selectPageTitle(state);
|
const pageTitle = selectPageTitle(state);
|
||||||
|
const scrollY = options.scrollY;
|
||||||
|
|
||||||
window.document.title = pageTitle;
|
window.document.title = pageTitle;
|
||||||
window.scrollTo(0, 0);
|
|
||||||
|
if (scrollY) window.scrollTo(0, scrollY);
|
||||||
|
else window.scrollTo(0, 0);
|
||||||
|
|
||||||
const currentPage = selectCurrentPage(state);
|
const currentPage = selectCurrentPage(state);
|
||||||
if (currentPage === "search") {
|
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) {
|
return function(dispatch, getState) {
|
||||||
title += " - LBRY";
|
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) {
|
return function(dispatch, getState) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
// Make a new directory within temp directory so the filename is guaranteed to be available
|
// 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 dir = fs.mkdtempSync(
|
||||||
const upgradeFilename = selectUpgradeFilename(state);
|
remote.app.getPath("temp") + require("path").sep
|
||||||
|
);
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
onProgress: p => dispatch(doUpdateDownloadProgress(Math.round(p * 100))),
|
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) {
|
export function doAlertError(errorList) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
@ -232,7 +265,8 @@ export function doAlertError(errorList) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doDaemonReady() {
|
export function doDaemonReady() {
|
||||||
return function(dispatch) {
|
return function(dispatch, getState) {
|
||||||
|
history.replaceState({}, document.title, `#/discover`);
|
||||||
dispatch(doAuthenticate());
|
dispatch(doAuthenticate());
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.DAEMON_READY,
|
type: types.DAEMON_READY,
|
||||||
|
@ -262,3 +296,10 @@ export function doClearCache() {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doQuitAndLaunchDaemonHelp() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
shell.openExternal("https://lbry.io/faq/incompatible-protocol-version");
|
||||||
|
remote.app.quit();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -159,22 +159,22 @@ export function doUserIdentityVerify(stripeToken) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
let user = selectUser(getState());
|
// let user = selectUser(getState());
|
||||||
user.is_identity_verified = true;
|
// user.is_identity_verified = true;
|
||||||
if (user.is_identity_verified) {
|
// 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({
|
// dispatch({
|
||||||
// type: types.USER_IDENTITY_VERIFY_FAILURE,
|
// type: types.USER_IDENTITY_VERIFY_SUCCESS,
|
||||||
// data: { error: error.toString() },
|
// 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() },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ import store from "store.js";
|
||||||
import lbry from "./lbry.js";
|
import lbry from "./lbry.js";
|
||||||
|
|
||||||
const env = ENV;
|
const env = ENV;
|
||||||
const config = require(`./config/${env}`);
|
const config = {
|
||||||
|
...require(`./config/${env}`),
|
||||||
|
};
|
||||||
const language = lbry.getClientSetting("language")
|
const language = lbry.getClientSetting("language")
|
||||||
? lbry.getClientSetting("language")
|
? lbry.getClientSetting("language")
|
||||||
: "en";
|
: "en";
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
doCheckUpgradeAvailable,
|
doCheckUpgradeAvailable,
|
||||||
doOpenModal,
|
doOpenModal,
|
||||||
doAlertError,
|
doAlertError,
|
||||||
|
doRecordScroll,
|
||||||
} from "actions/app";
|
} from "actions/app";
|
||||||
import { doUpdateBalance } from "actions/wallet";
|
import { doUpdateBalance } from "actions/wallet";
|
||||||
import { selectWelcomeModalAcknowledged } from "selectors/app";
|
import { selectWelcomeModalAcknowledged } from "selectors/app";
|
||||||
|
@ -36,6 +37,7 @@ const perform = dispatch => ({
|
||||||
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
||||||
openWelcomeModal: () => dispatch(doOpenModal(modals.WELCOME)),
|
openWelcomeModal: () => dispatch(doOpenModal(modals.WELCOME)),
|
||||||
updateBalance: balance => dispatch(doUpdateBalance(balance)),
|
updateBalance: balance => dispatch(doUpdateBalance(balance)),
|
||||||
|
recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(App);
|
export default connect(select, perform)(App);
|
||||||
|
|
|
@ -26,6 +26,10 @@ class App extends React.PureComponent {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showWelcome(this.props);
|
this.showWelcome(this.props);
|
||||||
|
|
||||||
|
this.scrollListener = () => this.props.recordScroll(window.scrollY);
|
||||||
|
|
||||||
|
window.addEventListener("scroll", this.scrollListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
@ -50,6 +54,10 @@ class App extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener("scroll", this.scrollListener);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { modal } = this.props;
|
const { modal } = this.props;
|
||||||
|
|
||||||
|
|
|
@ -78,10 +78,11 @@ class FileCard extends React.PureComponent {
|
||||||
onClick={() => navigate("/show", { uri })}
|
onClick={() => navigate("/show", { uri })}
|
||||||
className="card__link"
|
className="card__link"
|
||||||
>
|
>
|
||||||
|
<CardMedia title={title} thumbnail={thumbnail} />
|
||||||
<div className="card__title-identity">
|
<div className="card__title-identity">
|
||||||
<h5 title={title}>
|
<div className="card__title" title={title}>
|
||||||
<TruncatedText lines={1}>{title}</TruncatedText>
|
<TruncatedText lines={1}>{title}</TruncatedText>
|
||||||
</h5>
|
</div>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
<span style={{ float: "right" }}>
|
<span style={{ float: "right" }}>
|
||||||
<FilePrice uri={uri} />
|
<FilePrice uri={uri} />
|
||||||
|
@ -92,7 +93,6 @@ class FileCard extends React.PureComponent {
|
||||||
<UriIndicator uri={uri} />
|
<UriIndicator uri={uri} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CardMedia title={title} thumbnail={thumbnail} />
|
|
||||||
<div className="card__content card__subtext card__subtext--two-lines">
|
<div className="card__content card__subtext card__subtext--two-lines">
|
||||||
<TruncatedMarkdown lines={2}>{description}</TruncatedMarkdown>
|
<TruncatedMarkdown lines={2}>{description}</TruncatedMarkdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,7 +11,6 @@ const Link = props => {
|
||||||
icon,
|
icon,
|
||||||
badge,
|
badge,
|
||||||
button,
|
button,
|
||||||
hidden,
|
|
||||||
disabled,
|
disabled,
|
||||||
children,
|
children,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import ReactModal from "react-modal";
|
|
||||||
|
|
||||||
export class ModalPage extends React.PureComponent {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ReactModal
|
|
||||||
onCloseRequested={this.props.onAborted || this.props.onConfirmed}
|
|
||||||
{...this.props}
|
|
||||||
className={(this.props.className || "") + " modal-page"}
|
|
||||||
overlayClassName="modal-overlay"
|
|
||||||
>
|
|
||||||
<div className="modal-page__content">
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
</ReactModal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModalPage;
|
|
13
ui/js/component/modalIncompatibleDaemon/index.jsx
Normal file
13
ui/js/component/modalIncompatibleDaemon/index.jsx
Normal file
|
@ -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);
|
24
ui/js/component/modalIncompatibleDaemon/view.jsx
Normal file
24
ui/js/component/modalIncompatibleDaemon/view.jsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Modal } from "component/modal";
|
||||||
|
|
||||||
|
class ModalIncompatibleDaemon extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const { quitAndLaunchDaemonHelp } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={true}
|
||||||
|
contentLabel={__("Incompatible daemon running")}
|
||||||
|
type="alert"
|
||||||
|
confirmButtonLabel={__("Quit and Learn More")}
|
||||||
|
onConfirmed={quitAndLaunchDaemonHelp}
|
||||||
|
>
|
||||||
|
{__(
|
||||||
|
"This browser is running with an incompatible version of the LBRY protocol and your install must be repaired."
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalIncompatibleDaemon;
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import rewards from "rewards";
|
import rewards from "rewards";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doCloseModal, doNavigate } from "actions/app";
|
import { doCloseModal, doAuthNavigate } from "actions/app";
|
||||||
import { doSetClientSetting } from "actions/settings";
|
import { doSetClientSetting } from "actions/settings";
|
||||||
import { selectUserIsRewardApproved } from "selectors/user";
|
import { selectUserIsRewardApproved } from "selectors/user";
|
||||||
import {
|
import {
|
||||||
|
@ -30,7 +30,7 @@ const perform = dispatch => () => {
|
||||||
return {
|
return {
|
||||||
verifyAccount: () => {
|
verifyAccount: () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
dispatch(doNavigate("/auth"));
|
dispatch(doAuthNavigate("/rewards"));
|
||||||
},
|
},
|
||||||
closeModal: closeModal,
|
closeModal: closeModal,
|
||||||
};
|
};
|
||||||
|
|
|
@ -93,6 +93,7 @@ class ChannelSection extends React.PureComponent {
|
||||||
"This LBC remains yours and the deposit can be undone at any time."
|
"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;
|
const { fetchingChannels, channels = [] } = this.props;
|
||||||
|
|
||||||
let channelContent = [];
|
let channelContent = [];
|
||||||
|
@ -102,7 +103,7 @@ class ChannelSection extends React.PureComponent {
|
||||||
type="select"
|
type="select"
|
||||||
tabIndex="1"
|
tabIndex="1"
|
||||||
onChange={this.handleChannelChange.bind(this)}
|
onChange={this.handleChannelChange.bind(this)}
|
||||||
value={this.props.channel}
|
value={channel}
|
||||||
>
|
>
|
||||||
<option key="anonymous" value="anonymous">
|
<option key="anonymous" value="anonymous">
|
||||||
{__("Anonymous")}
|
{__("Anonymous")}
|
||||||
|
@ -111,7 +112,7 @@ class ChannelSection extends React.PureComponent {
|
||||||
<option key={name} value={name}>{name}</option>
|
<option key={name} value={name}>{name}</option>
|
||||||
)}
|
)}
|
||||||
<option key="new" value="new">
|
<option key="new" value="new">
|
||||||
{__("New identity...")}
|
{__("New channel...")}
|
||||||
</option>
|
</option>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
);
|
);
|
||||||
|
@ -124,9 +125,10 @@ class ChannelSection extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__title-primary">
|
<div className="card__title-primary">
|
||||||
<h4>{__("Identity")}</h4>
|
<h4>{__("Channel Name")}</h4>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
{__("Who created this content?")}
|
{__("This is the channel that broadcasts your content.")}
|
||||||
|
{__("Ex. @Marvel, @TheBeatles, @BooksByJoe")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
|
@ -137,9 +139,7 @@ class ChannelSection extends React.PureComponent {
|
||||||
<FormRow
|
<FormRow
|
||||||
label={__("Name")}
|
label={__("Name")}
|
||||||
type="text"
|
type="text"
|
||||||
onChange={event => {
|
onChange={this.handleNewChannelNameChange.bind(this)}
|
||||||
this.handleNewChannelNameChange(event);
|
|
||||||
}}
|
|
||||||
value={this.state.newChannelName}
|
value={this.state.newChannelName}
|
||||||
/>
|
/>
|
||||||
<FormRow
|
<FormRow
|
||||||
|
@ -158,8 +158,8 @@ class ChannelSection extends React.PureComponent {
|
||||||
button="primary"
|
button="primary"
|
||||||
label={
|
label={
|
||||||
!this.state.creatingChannel
|
!this.state.creatingChannel
|
||||||
? __("Create identity")
|
? __("Create channel")
|
||||||
: __("Creating identity...")
|
: __("Creating channel...")
|
||||||
}
|
}
|
||||||
onClick={this.handleCreateChannelClick.bind(this)}
|
onClick={this.handleCreateChannelClick.bind(this)}
|
||||||
disabled={this.state.creatingChannel}
|
disabled={this.state.creatingChannel}
|
||||||
|
|
|
@ -42,6 +42,7 @@ class PublishForm extends React.PureComponent {
|
||||||
submitting: false,
|
submitting: false,
|
||||||
creatingChannel: false,
|
creatingChannel: false,
|
||||||
modal: null,
|
modal: null,
|
||||||
|
isFee: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,11 +636,8 @@ class PublishForm extends React.PureComponent {
|
||||||
label={__("Free")}
|
label={__("Free")}
|
||||||
type="radio"
|
type="radio"
|
||||||
name="isFree"
|
name="isFree"
|
||||||
value="1"
|
onChange={() => this.handleFeePrefChange(false)}
|
||||||
onChange={() => {
|
checked={!this.state.isFee}
|
||||||
this.handleFeePrefChange(false);
|
|
||||||
}}
|
|
||||||
defaultChecked={!this.state.isFee}
|
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
type="radio"
|
type="radio"
|
||||||
|
@ -648,7 +646,7 @@ class PublishForm extends React.PureComponent {
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
this.handleFeePrefChange(true);
|
this.handleFeePrefChange(true);
|
||||||
}}
|
}}
|
||||||
defaultChecked={this.state.isFee}
|
checked={this.state.isFee}
|
||||||
/>
|
/>
|
||||||
<span className={!this.state.isFee ? "hidden" : ""}>
|
<span className={!this.state.isFee ? "hidden" : ""}>
|
||||||
<FormField
|
<FormField
|
||||||
|
@ -789,12 +787,14 @@ class PublishForm extends React.PureComponent {
|
||||||
|
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__title-primary">
|
<div className="card__title-primary">
|
||||||
<h4>{__("Address")}</h4>
|
<h4>{__("Content URL")}</h4>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
{__("Where should this content permanently reside?")}
|
{__(
|
||||||
|
"This is the exact address where people find your content (ex. lbry://myvideo)."
|
||||||
|
)}
|
||||||
{" "}
|
{" "}
|
||||||
<Link
|
<Link
|
||||||
label={__("Read more")}
|
label={__("Learn more")}
|
||||||
href="https://lbry.io/faq/naming"
|
href="https://lbry.io/faq/naming"
|
||||||
/>.
|
/>.
|
||||||
</div>
|
</div>
|
||||||
|
|
19
ui/js/component/splash/index.js
Normal file
19
ui/js/component/splash/index.js
Normal file
|
@ -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);
|
|
@ -1,6 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import lbry from "../lbry.js";
|
import lbry from "../../lbry.js";
|
||||||
import LoadScreen from "./load_screen.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 {
|
export class SplashScreen extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -14,6 +17,7 @@ export class SplashScreen extends React.PureComponent {
|
||||||
this.state = {
|
this.state = {
|
||||||
details: __("Starting daemon"),
|
details: __("Starting daemon"),
|
||||||
message: __("Connecting"),
|
message: __("Connecting"),
|
||||||
|
isRunning: false,
|
||||||
isLagging: false,
|
isLagging: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -35,10 +39,16 @@ export class SplashScreen extends React.PureComponent {
|
||||||
message: __("Testing Network"),
|
message: __("Testing Network"),
|
||||||
details: __("Waiting for name resolution"),
|
details: __("Waiting for name resolution"),
|
||||||
isLagging: false,
|
isLagging: false,
|
||||||
|
isRunning: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
lbry.resolve({ uri: "lbry://one" }).then(() => {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +64,7 @@ export class SplashScreen extends React.PureComponent {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
lbry
|
lbry
|
||||||
.connect()
|
.connect()
|
||||||
|
.then(this.props.checkDaemonVersion)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.updateStatus();
|
this.updateStatus();
|
||||||
})
|
})
|
||||||
|
@ -69,12 +80,24 @@ export class SplashScreen extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { modal } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div>
|
||||||
<LoadScreen
|
<LoadScreen
|
||||||
message={this.state.message}
|
message={this.state.message}
|
||||||
details={this.state.details}
|
details={this.state.details}
|
||||||
isWarning={this.state.isLagging}
|
isWarning={this.state.isLagging}
|
||||||
/>
|
/>
|
||||||
|
{/* 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 &&
|
||||||
|
<ModalIncompatibleDaemon />}
|
||||||
|
{modal == "upgrade" && this.state.isRunning && <ModalUpgrade />}
|
||||||
|
{modal == "downloading" && this.state.isRunning && <ModalDownloading />}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,15 +25,13 @@ class UserVerify extends React.PureComponent {
|
||||||
const { errorMessage, isPending, reward } = this.props;
|
const { errorMessage, isPending, reward } = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
{(!reward || !reward.transaction_id) &&
|
||||||
<p>
|
<p>
|
||||||
<span>
|
|
||||||
Please link a credit card to confirm your identity and receive{" "}
|
Please link a credit card to confirm your identity and receive{" "}
|
||||||
</span>
|
|
||||||
{reward
|
{reward
|
||||||
? <CreditAmount amount={parseFloat(reward.reward_amount)} />
|
? <CreditAmount amount={parseFloat(reward.reward_amount)} />
|
||||||
: <span>your reward</span>}
|
: <span>your reward</span>}
|
||||||
{"."}
|
</p>}
|
||||||
</p>
|
|
||||||
<p>{__("This is to prevent abuse. You will not be charged.")}</p>
|
<p>{__("This is to prevent abuse. You will not be charged.")}</p>
|
||||||
{errorMessage && <p className="form-field__error">{errorMessage}</p>}
|
{errorMessage && <p className="form-field__error">{errorMessage}</p>}
|
||||||
<CardVerify
|
<CardVerify
|
||||||
|
|
|
@ -7,6 +7,8 @@ export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK";
|
||||||
export const WINDOW_FOCUSED = "WINDOW_FOCUSED";
|
export const WINDOW_FOCUSED = "WINDOW_FOCUSED";
|
||||||
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
|
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
|
||||||
export const DAEMON_READY = "DAEMON_READY";
|
export const DAEMON_READY = "DAEMON_READY";
|
||||||
|
export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH";
|
||||||
|
export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH";
|
||||||
|
|
||||||
// Upgrades
|
// Upgrades
|
||||||
export const UPGRADE_CANCELLED = "UPGRADE_CANCELLED";
|
export const UPGRADE_CANCELLED = "UPGRADE_CANCELLED";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export const CONFIRM_FILE_REMOVE = "confirmFileRemove";
|
export const CONFIRM_FILE_REMOVE = "confirmFileRemove";
|
||||||
|
export const INCOMPATIBLE_DAEMON = "incompatibleDaemon";
|
||||||
export const DOWNLOADING = "downloading";
|
export const DOWNLOADING = "downloading";
|
||||||
export const ERROR = "error";
|
export const ERROR = "error";
|
||||||
export const INSUFFICIENT_CREDITS = "insufficient_credits";
|
export const INSUFFICIENT_CREDITS = "insufficient_credits";
|
||||||
|
|
|
@ -138,6 +138,7 @@ lbryio.authenticate = function() {
|
||||||
language: "en",
|
language: "en",
|
||||||
has_email: true,
|
has_email: true,
|
||||||
has_verified_email: true,
|
has_verified_email: true,
|
||||||
|
is_identity_verified: true,
|
||||||
is_reward_approved: false,
|
is_reward_approved: false,
|
||||||
is_reward_eligible: false,
|
is_reward_eligible: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import App from "component/app/index.js";
|
||||||
import SnackBar from "component/snackBar";
|
import SnackBar from "component/snackBar";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import store from "store.js";
|
import store from "store.js";
|
||||||
import SplashScreen from "component/splash.js";
|
import SplashScreen from "component/splash";
|
||||||
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
|
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
|
||||||
import { toQueryString } from "util/query_params";
|
import { toQueryString } from "util/query_params";
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
|
@ -35,10 +35,10 @@ window.addEventListener("popstate", (event, param) => {
|
||||||
|
|
||||||
if (hash !== "") {
|
if (hash !== "") {
|
||||||
const url = hash.split("#")[1];
|
const url = hash.split("#")[1];
|
||||||
const params = event.state;
|
const { params, scrollY } = event.state || {};
|
||||||
const queryString = toQueryString(params);
|
const queryString = toQueryString(params);
|
||||||
|
|
||||||
app.store.dispatch(doChangePath(`${url}?${queryString}`));
|
app.store.dispatch(doChangePath(`${url}?${queryString}`, { scrollY }));
|
||||||
} else {
|
} else {
|
||||||
app.store.dispatch(doChangePath("/discover"));
|
app.store.dispatch(doChangePath("/discover"));
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,12 @@ var init = function() {
|
||||||
if (window.sessionStorage.getItem("loaded") == "y") {
|
if (window.sessionStorage.getItem("loaded") == "y") {
|
||||||
onDaemonReady();
|
onDaemonReady();
|
||||||
} else {
|
} else {
|
||||||
ReactDOM.render(<SplashScreen onLoadDone={onDaemonReady} />, canvas);
|
ReactDOM.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<SplashScreen onReadyToLaunch={onDaemonReady} />
|
||||||
|
</Provider>,
|
||||||
|
canvas
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ const makeSelect = () => {
|
||||||
contentType: selectContentType(state, props),
|
contentType: selectContentType(state, props),
|
||||||
costInfo: selectCostInfo(state, props),
|
costInfo: selectCostInfo(state, props),
|
||||||
metadata: selectMetadata(state, props),
|
metadata: selectMetadata(state, props),
|
||||||
showNsfw: !selectShowNsfw(state),
|
obscureNsfw: !selectShowNsfw(state),
|
||||||
fileInfo: selectFileInfo(state, props),
|
fileInfo: selectFileInfo(state, props),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@ import {
|
||||||
selectRewards,
|
selectRewards,
|
||||||
} from "selectors/rewards";
|
} from "selectors/rewards";
|
||||||
import {
|
import {
|
||||||
selectUserIsRewardEligible,
|
selectUser,
|
||||||
selectUserHasEmail,
|
selectUserHasEmail,
|
||||||
selectUserIsVerificationCandidate,
|
selectUserIsVerificationCandidate,
|
||||||
} from "selectors/user";
|
} from "selectors/user";
|
||||||
import { doAuthNavigate } from "actions/app";
|
import { doAuthNavigate, doNavigate } from "actions/app";
|
||||||
import { doRewardList } from "actions/rewards";
|
import { doRewardList } from "actions/rewards";
|
||||||
import rewards from "rewards";
|
import rewards from "rewards";
|
||||||
import RewardsPage from "./view";
|
import RewardsPage from "./view";
|
||||||
|
@ -21,15 +21,14 @@ const select = (state, props) => {
|
||||||
return {
|
return {
|
||||||
fetching: selectFetchingRewards(state),
|
fetching: selectFetchingRewards(state),
|
||||||
rewards: selectRewards(state),
|
rewards: selectRewards(state),
|
||||||
hasEmail: selectUserHasEmail(state),
|
|
||||||
isEligible: selectUserIsRewardEligible(state),
|
|
||||||
isVerificationCandidate: selectUserIsVerificationCandidate(state),
|
|
||||||
newUserReward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
newUserReward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||||
|
user: selectUser(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchRewards: () => dispatch(doRewardList()),
|
fetchRewards: () => dispatch(doRewardList()),
|
||||||
|
navigate: path => dispatch(doNavigate(path)),
|
||||||
doAuth: () => {
|
doAuth: () => {
|
||||||
dispatch(doAuthNavigate("/rewards"));
|
dispatch(doAuthNavigate("/rewards"));
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,39 +45,11 @@ class RewardsPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { doAuth, fetching, navigate, rewards, user } = this.props;
|
||||||
doAuth,
|
|
||||||
fetching,
|
|
||||||
isEligible,
|
|
||||||
isVerificationCandidate,
|
|
||||||
hasEmail,
|
|
||||||
rewards,
|
|
||||||
newUserReward,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let content,
|
let content, cardHeader;
|
||||||
isCard = false;
|
|
||||||
|
|
||||||
if (!hasEmail || isVerificationCandidate) {
|
if (fetching) {
|
||||||
content = (
|
|
||||||
<div>
|
|
||||||
<div className="card__content empty">
|
|
||||||
<p>{__("Only verified accounts are eligible to earn rewards.")}</p>
|
|
||||||
</div>
|
|
||||||
<div className="card__content">
|
|
||||||
<Link onClick={doAuth} button="primary" label="Become Verified" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
isCard = true;
|
|
||||||
} else if (!isEligible) {
|
|
||||||
isCard = true;
|
|
||||||
content = (
|
|
||||||
<div className="card__content empty">
|
|
||||||
<p>{__("You are not eligible to claim rewards.")}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (fetching) {
|
|
||||||
content = (
|
content = (
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<BusyMessage message={__("Fetching rewards")} />
|
<BusyMessage message={__("Fetching rewards")} />
|
||||||
|
@ -99,10 +71,54 @@ class RewardsPage extends React.PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
user &&
|
||||||
|
(!user.has_email ||
|
||||||
|
!user.has_verified_email ||
|
||||||
|
!user.is_identity_verified)
|
||||||
|
) {
|
||||||
|
cardHeader = (
|
||||||
|
<div>
|
||||||
|
<div className="card__content empty">
|
||||||
|
<p>{__("Only verified accounts are eligible to earn rewards.")}</p>
|
||||||
|
</div>
|
||||||
|
<div className="card__content">
|
||||||
|
<Link onClick={doAuth} button="primary" label="Become Verified" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (user && !user.is_reward_approved) {
|
||||||
|
cardHeader = (
|
||||||
|
<div className="card__content">
|
||||||
|
<p>
|
||||||
|
{__(
|
||||||
|
"This account must undergo review before you can participate in the rewards program."
|
||||||
|
)}
|
||||||
|
{" "}
|
||||||
|
{__("This can take anywhere from several minutes to several days.")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{__("You will receive an email when this process is complete.")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{__("Please enjoy free content in the meantime!")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<Link
|
||||||
|
onClick={() => navigate("/discover")}
|
||||||
|
button="primary"
|
||||||
|
label="Return Home"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<SubHeader />
|
<SubHeader />
|
||||||
{isCard ? <section className="card">{content}</section> : content}
|
{cardHeader && <section className="card">{cardHeader}</section>}
|
||||||
|
{content}
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
|
import * as modalTypes from "constants/modal_types";
|
||||||
import lbry from "lbry";
|
import lbry from "lbry";
|
||||||
|
|
||||||
const currentPath = () => {
|
const currentPath = () => {
|
||||||
|
@ -18,6 +19,7 @@ const defaultState = {
|
||||||
pathAfterAuth: "/discover",
|
pathAfterAuth: "/discover",
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
|
upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
|
||||||
|
daemonVersionMatched: null,
|
||||||
daemonReady: false,
|
daemonReady: false,
|
||||||
hasSignature: false,
|
hasSignature: false,
|
||||||
badgeNumber: 0,
|
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) {
|
reducers[types.CHANGE_PATH] = function(state, action) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
currentPath: action.data.path,
|
currentPath: action.data.path,
|
||||||
|
|
|
@ -177,6 +177,11 @@ export const selectDaemonReady = createSelector(
|
||||||
state => state.daemonReady
|
state => state.daemonReady
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectDaemonVersionMatched = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => state.daemonVersionMatched
|
||||||
|
);
|
||||||
|
|
||||||
export const selectSnackBar = createSelector(
|
export const selectSnackBar = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
state => state.snackBar || {}
|
state => state.snackBar || {}
|
||||||
|
|
|
@ -30,11 +30,6 @@ export const selectUserHasEmail = createSelector(
|
||||||
(user, email) => (user && user.has_email) || !!email
|
(user, email) => (user && user.has_email) || !!email
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectUserIsRewardEligible = createSelector(
|
|
||||||
selectUser,
|
|
||||||
user => user && user.is_reward_eligible
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectUserIsRewardApproved = createSelector(
|
export const selectUserIsRewardApproved = createSelector(
|
||||||
selectUser,
|
selectUser,
|
||||||
user => user && user.is_reward_approved
|
user => user && user.is_reward_approved
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const { remote } = require("electron");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thin wrapper around localStorage.getItem(). Parses JSON and returns undefined if the value
|
* Thin wrapper around localStorage.getItem(). Parses JSON and returns undefined if the value
|
||||||
* is not set yet.
|
* is not set yet.
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
@import "component/_channel-indicator.scss";
|
@import "component/_channel-indicator.scss";
|
||||||
@import "component/_notice.scss";
|
@import "component/_notice.scss";
|
||||||
@import "component/_modal.scss";
|
@import "component/_modal.scss";
|
||||||
@import "component/_modal-page.scss";
|
|
||||||
@import "component/_snack-bar.scss";
|
@import "component/_snack-bar.scss";
|
||||||
@import "component/_video.scss";
|
@import "component/_video.scss";
|
||||||
@import "page/_developer.scss";
|
@import "page/_developer.scss";
|
||||||
|
|
|
@ -19,18 +19,29 @@ $padding-card-horizontal: $spacing-vertical * 2/3;
|
||||||
.card--obscured .card__inner {
|
.card--obscured .card__inner {
|
||||||
filter: blur($blur-intensity-nsfw);
|
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;
|
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;
|
margin-top: $spacing-vertical * 2/3;
|
||||||
}
|
}
|
||||||
.card__title-identity {
|
.card__title-identity {
|
||||||
padding: 0 $padding-card-horizontal;
|
|
||||||
margin-top: $spacing-vertical * 1/3;
|
margin-top: $spacing-vertical * 1/3;
|
||||||
margin-bottom: $spacing-vertical * 1/3;
|
margin-bottom: $spacing-vertical * 1/3;
|
||||||
}
|
}
|
||||||
.card__actions {
|
|
||||||
padding: 0 $padding-card-horizontal;
|
|
||||||
}
|
|
||||||
.card__actions {
|
.card__actions {
|
||||||
margin-top: $spacing-vertical * 2/3;
|
margin-top: $spacing-vertical * 2/3;
|
||||||
}
|
}
|
||||||
|
@ -45,21 +56,18 @@ $padding-card-horizontal: $spacing-vertical * 2/3;
|
||||||
.card__content {
|
.card__content {
|
||||||
margin-top: $spacing-vertical * 2/3;
|
margin-top: $spacing-vertical * 2/3;
|
||||||
margin-bottom: $spacing-vertical * 2/3;
|
margin-bottom: $spacing-vertical * 2/3;
|
||||||
padding: 0 $padding-card-horizontal;
|
|
||||||
}
|
}
|
||||||
.card__subtext {
|
.card__subtext {
|
||||||
color: #444;
|
color: $color-meta-light;
|
||||||
margin-top: 12px;
|
font-size: 0.82em;
|
||||||
font-size: 0.9em;
|
margin-top: $spacing-vertical * 1/3;
|
||||||
margin-top: $spacing-vertical * 2/3;
|
margin-bottom: $spacing-vertical * 1/3;
|
||||||
margin-bottom: $spacing-vertical * 2/3;
|
|
||||||
padding: 0 $padding-card-horizontal;
|
|
||||||
}
|
}
|
||||||
.card__subtext--allow-newlines {
|
.card__subtext--allow-newlines {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
.card__subtext--two-lines {
|
.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 {
|
.card-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -144,7 +152,7 @@ $card-link-scaling: 1.1;
|
||||||
top: 36%
|
top: 36%
|
||||||
}
|
}
|
||||||
|
|
||||||
$width-card-small: $spacing-vertical * 12;
|
$width-card-small: $spacing-vertical * 10;
|
||||||
$height-card-small: $spacing-vertical * 15;
|
$height-card-small: $spacing-vertical * 15;
|
||||||
|
|
||||||
.card--small {
|
.card--small {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue