Merge branch 'master' into auto-update
This commit is contained in:
commit
f302a0f92a
48 changed files with 646 additions and 96 deletions
|
@ -36,6 +36,7 @@
|
|||
"amplitude-js": "^4.0.0",
|
||||
"bluebird": "^3.5.1",
|
||||
"classnames": "^2.2.5",
|
||||
"country-data": "^0.0.31",
|
||||
"electron-dl": "^1.6.0",
|
||||
"electron-log": "^2.2.12",
|
||||
"electron-publisher-s3": "^19.47.0",
|
||||
|
@ -47,6 +48,7 @@
|
|||
"jshashes": "^1.0.7",
|
||||
"keytar-prebuild": "^4.0.4",
|
||||
"localforage": "^1.5.0",
|
||||
"moment": "^2.20.1",
|
||||
"npm": "^5.5.1",
|
||||
"qrcode.react": "^0.7.2",
|
||||
"rc-progress": "^2.0.6",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri.js';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import CardMedia from 'component/cardMedia';
|
||||
import Link from 'component/link';
|
||||
import { TruncatedText } from 'component/common';
|
||||
|
@ -57,7 +57,7 @@ class FileCard extends React.PureComponent {
|
|||
rewardedContentClaimIds,
|
||||
} = this.props;
|
||||
|
||||
const uri = lbryuri.normalize(this.props.uri);
|
||||
const uri = normalizeURI(this.props.uri);
|
||||
const title = metadata && metadata.title ? metadata.title : uri;
|
||||
const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null;
|
||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri.js';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import FormField from 'component/formField';
|
||||
import FileTile from 'component/fileTile';
|
||||
import { BusyMessage } from 'component/common.js';
|
||||
|
@ -76,7 +76,7 @@ class FileList extends React.PureComponent {
|
|||
uriParams.claimId = fileInfo.claim_id;
|
||||
uriParams.name = fileInfo.name;
|
||||
}
|
||||
const uri = lbryuri.build(uriParams);
|
||||
const uri = buildURI(uriParams);
|
||||
|
||||
content.push(
|
||||
<FileTile
|
||||
|
|
|
@ -3,7 +3,7 @@ import FileTile from 'component/fileTile';
|
|||
import ChannelTile from 'component/channelTile';
|
||||
import Link from 'component/link';
|
||||
import { BusyMessage } from 'component/common.js';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { parseURI } from 'lbryURI';
|
||||
|
||||
const SearchNoResults = props => {
|
||||
const { query } = props;
|
||||
|
@ -45,7 +45,7 @@ class FileListSearch extends React.PureComponent {
|
|||
{uris && uris.length
|
||||
? uris.map(
|
||||
uri =>
|
||||
lbryuri.parse(uri).name[0] === '@' ? (
|
||||
parseURI(uri).name[0] === '@' ? (
|
||||
<ChannelTile key={uri} uri={uri} />
|
||||
) : (
|
||||
<FileTile key={uri} uri={uri} />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import * as icons from 'constants/icons';
|
||||
import lbryuri from 'lbryuri.js';
|
||||
import { normalizeURI, isURIClaimable, parseURI } from 'lbryURI';
|
||||
import CardMedia from 'component/cardMedia';
|
||||
import { TruncatedText } from 'component/common.js';
|
||||
import FilePrice from 'component/filePrice';
|
||||
|
@ -65,11 +65,11 @@ class FileTile extends React.PureComponent {
|
|||
fileInfo,
|
||||
} = this.props;
|
||||
|
||||
const uri = lbryuri.normalize(this.props.uri);
|
||||
const uri = normalizeURI(this.props.uri);
|
||||
const isClaimed = !!claim;
|
||||
const isClaimable = lbryuri.isClaimable(uri);
|
||||
const isClaimable = isURIClaimable(uri);
|
||||
const title =
|
||||
isClaimed && metadata && metadata.title ? metadata.title : lbryuri.parse(uri).contentName;
|
||||
isClaimed && metadata && metadata.title ? metadata.title : parseURI(uri).contentName;
|
||||
const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null;
|
||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { isNameValid } from 'lbryURI';
|
||||
import { FormRow } from 'component/form.js';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import Link from 'component/link';
|
||||
|
@ -29,7 +29,7 @@ class ChannelSection extends React.PureComponent {
|
|||
? event.target.value
|
||||
: `@${event.target.value}`;
|
||||
|
||||
if (newChannelName.length > 1 && !lbryuri.isValidName(newChannelName.substr(1), false)) {
|
||||
if (newChannelName.length > 1 && !isNameValid(newChannelName.substr(1), false)) {
|
||||
this.refs.newChannelName.showError(
|
||||
__('LBRY channel names must contain only letters, numbers and dashes.')
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { isNameValid, buildURI, regexInvalidURI } from 'lbryURI';
|
||||
import FormField from 'component/formField';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
import Link from 'component/link';
|
||||
|
@ -245,7 +245,7 @@ class PublishForm extends React.PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!lbryuri.isValidName(rawName, false)) {
|
||||
if (!isNameValid(rawName, false)) {
|
||||
this.refs.name.showError(__('LBRY names must contain only letters, numbers and dashes.'));
|
||||
return;
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ class PublishForm extends React.PureComponent {
|
|||
if (this.state.channel !== 'anonymous') channel = this.state.channel;
|
||||
|
||||
const name = rawName.toLowerCase();
|
||||
const uri = lbryuri.build({ contentName: name, channelName: channel });
|
||||
const uri = buildURI({ contentName: name, channelName: channel });
|
||||
this.setState({
|
||||
rawName,
|
||||
name,
|
||||
|
@ -446,7 +446,7 @@ class PublishForm extends React.PureComponent {
|
|||
const extension = path.extname(fileName);
|
||||
|
||||
fileName = path.basename(fileName, extension);
|
||||
fileName = fileName.replace(lbryuri.REGEXP_INVALID_URI, '');
|
||||
fileName = fileName.replace(regexInvalidURI, '');
|
||||
return fileName;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import LinkTransaction from 'component/linkTransaction';
|
|||
import { CreditAmount } from 'component/common';
|
||||
import DateTime from 'component/dateTime';
|
||||
import Link from 'component/link';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import * as txnTypes from 'constants/transaction_types';
|
||||
|
||||
class TransactionListItem extends React.PureComponent {
|
||||
|
@ -74,7 +74,7 @@ class TransactionListItem extends React.PureComponent {
|
|||
<Link
|
||||
className="button-text"
|
||||
navigate="/show"
|
||||
navigateParams={{ uri: lbryuri.build({ name, claimId }) }}
|
||||
navigateParams={{ uri: buildURI({ name, claimId }) }}
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { connect } from 'react-redux';
|
||||
import { doResolveUri } from 'redux/actions/content';
|
||||
import { makeSelectIsUriResolving } from 'redux/selectors/content';
|
||||
|
@ -9,7 +9,7 @@ import UriIndicator from './view';
|
|||
const select = (state, props) => ({
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||
uri: lbryuri.normalize(props.uri),
|
||||
uri: normalizeURI(props.uri),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import Icon from 'component/icon';
|
||||
import Link from 'component/link';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import classnames from 'classnames';
|
||||
|
||||
class UriIndicator extends React.PureComponent {
|
||||
|
@ -49,7 +49,7 @@ class UriIndicator extends React.PureComponent {
|
|||
|
||||
if (signatureIsValid) {
|
||||
modifier = 'valid';
|
||||
channelLink = link ? lbryuri.build({ channelName, claimId: channelClaimId }, false) : false;
|
||||
channelLink = link ? buildURI({ channelName, claimId: channelClaimId }, false) : false;
|
||||
} else {
|
||||
icon = 'icon-times-circle';
|
||||
modifier = 'invalid';
|
||||
|
|
15
src/renderer/component/userPhoneNew/index.js
Normal file
15
src/renderer/component/userPhoneNew/index.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doUserPhoneNew } from 'redux/actions/user';
|
||||
import { selectPhoneNewErrorMessage } from 'redux/selectors/user';
|
||||
import UserPhoneNew from './view';
|
||||
|
||||
const select = state => ({
|
||||
phoneErrorMessage: selectPhoneNewErrorMessage(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
addUserPhone: (phone, country_code) => dispatch(doUserPhoneNew(phone, country_code)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(UserPhoneNew);
|
109
src/renderer/component/userPhoneNew/view.jsx
Normal file
109
src/renderer/component/userPhoneNew/view.jsx
Normal file
|
@ -0,0 +1,109 @@
|
|||
import React from 'react';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
import FormField from 'component/formField';
|
||||
|
||||
const os = require('os').type();
|
||||
const countryCodes = require('country-data')
|
||||
.callingCountries.all.filter(_ => _.emoji)
|
||||
.reduce(
|
||||
(acc, cur) => acc.concat(cur.countryCallingCodes.map(_ => ({ ...cur, countryCallingCode: _ }))),
|
||||
[]
|
||||
)
|
||||
.sort((a, b) => {
|
||||
if (a.countryCallingCode < b.countryCallingCode) {
|
||||
return -1;
|
||||
}
|
||||
if (a.countryCallingCode > b.countryCallingCode) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
class UserPhoneNew extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
phone: '',
|
||||
country_code: '+1',
|
||||
};
|
||||
|
||||
this.formatPhone = this.formatPhone.bind(this);
|
||||
}
|
||||
|
||||
formatPhone(value) {
|
||||
const { country_code } = this.state;
|
||||
value = value.replace(/\D/g, '');
|
||||
if (country_code === '+1') {
|
||||
if (!value) {
|
||||
return '';
|
||||
} else if (value.length < 4) {
|
||||
return value;
|
||||
} else if (value.length < 7) {
|
||||
return `(${value.substring(0, 3)}) ${value.substring(3)}`;
|
||||
}
|
||||
const fullNumber = `(${value.substring(0, 3)}) ${value.substring(3, 6)}-${value.substring(
|
||||
6
|
||||
)}`;
|
||||
return fullNumber.length <= 14 ? fullNumber : fullNumber.substring(0, 14);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
handleChanged(event) {
|
||||
this.setState({
|
||||
phone: this.formatPhone(event.target.value),
|
||||
});
|
||||
}
|
||||
|
||||
handleSelect(event) {
|
||||
this.setState({ country_code: event.target.value });
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
const { phone, country_code } = this.state;
|
||||
this.props.addUserPhone(phone.replace(/\D/g, ''), country_code.substring(1));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { cancelButton, phoneErrorMessage, isPending } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{__(
|
||||
'Enter your phone number and we will send you a verification code. We will not share your phone number with third parties.'
|
||||
)}
|
||||
</p>
|
||||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<div className="form-row-phone">
|
||||
<FormField type="select" onChange={this.handleSelect.bind(this)}>
|
||||
{countryCodes.map((country, index) => (
|
||||
<option key={index} value={country.countryCallingCode}>
|
||||
{os === 'Darwin' ? country.emoji : `(${country.alpha2})`}{' '}
|
||||
{country.countryCallingCode}
|
||||
</option>
|
||||
))}
|
||||
</FormField>
|
||||
<FormRow
|
||||
type="text"
|
||||
placeholder={this.state.country_code === '+1' ? '(555) 555-5555' : '5555555555'}
|
||||
name="phone"
|
||||
value={this.state.phone}
|
||||
errorMessage={phoneErrorMessage}
|
||||
onChange={event => {
|
||||
this.handleChanged(event);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-row-submit">
|
||||
<Submit label="Submit" disabled={isPending} />
|
||||
{cancelButton}
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserPhoneNew;
|
22
src/renderer/component/userPhoneVerify/index.js
Normal file
22
src/renderer/component/userPhoneVerify/index.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doUserPhoneVerify, doUserPhoneReset } from 'redux/actions/user';
|
||||
import {
|
||||
selectPhoneToVerify,
|
||||
selectPhoneVerifyErrorMessage,
|
||||
selectUserCountryCode,
|
||||
} from 'redux/selectors/user';
|
||||
import UserPhoneVerify from './view';
|
||||
|
||||
const select = state => ({
|
||||
phone: selectPhoneToVerify(state),
|
||||
countryCode: selectUserCountryCode(state),
|
||||
phoneErrorMessage: selectPhoneVerifyErrorMessage(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
resetPhone: () => dispatch(doUserPhoneReset()),
|
||||
verifyUserPhone: code => dispatch(doUserPhoneVerify(code)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(UserPhoneVerify);
|
69
src/renderer/component/userPhoneVerify/view.jsx
Normal file
69
src/renderer/component/userPhoneVerify/view.jsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
|
||||
class UserPhoneVerify extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
code: '',
|
||||
};
|
||||
}
|
||||
|
||||
handleCodeChanged(event) {
|
||||
this.setState({
|
||||
code: String(event.target.value).trim(),
|
||||
});
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
const { code } = this.state;
|
||||
this.props.verifyUserPhone(code);
|
||||
}
|
||||
|
||||
reset() {
|
||||
const { resetPhone } = this.props;
|
||||
resetPhone();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { cancelButton, phoneErrorMessage, phone, countryCode } = this.props;
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<p>
|
||||
{__(
|
||||
`Please enter the verification code sent to +${countryCode}${
|
||||
phone
|
||||
}. Didn't receive it? `
|
||||
)}
|
||||
<Link onClick={this.reset.bind(this)} label="Go back." />
|
||||
</p>
|
||||
<FormRow
|
||||
type="text"
|
||||
label={__('Verification Code')}
|
||||
name="code"
|
||||
value={this.state.code}
|
||||
onChange={event => {
|
||||
this.handleCodeChanged(event);
|
||||
}}
|
||||
errorMessage={phoneErrorMessage}
|
||||
/>
|
||||
{/* render help separately so it always shows */}
|
||||
<div className="form-field__helper">
|
||||
<p>
|
||||
{__('Email')} <Link href="mailto:help@lbry.io" label="help@lbry.io" /> or join our{' '}
|
||||
<Link href="https://chat.lbry.io" label="chat" />{' '}
|
||||
{__('if you encounter any trouble with your code.')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="form-row-submit">
|
||||
<Submit label={__('Verify')} />
|
||||
{cancelButton}
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserPhoneVerify;
|
|
@ -9,6 +9,9 @@ import {
|
|||
selectIdentityVerifyErrorMessage,
|
||||
} from 'redux/selectors/user';
|
||||
import UserVerify from './view';
|
||||
import { selectCurrentModal } from 'redux/selectors/app';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { PHONE_COLLECTION } from 'constants/modal_types';
|
||||
|
||||
const select = (state, props) => {
|
||||
const selectReward = makeSelectRewardByType();
|
||||
|
@ -17,12 +20,14 @@ const select = (state, props) => {
|
|||
isPending: selectIdentityVerifyIsPending(state),
|
||||
errorMessage: selectIdentityVerifyErrorMessage(state),
|
||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
modal: selectCurrentModal(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
navigate: uri => dispatch(doNavigate(uri)),
|
||||
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
|
||||
verifyPhone: () => dispatch(doOpenModal(PHONE_COLLECTION)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(UserVerify);
|
||||
|
|
|
@ -23,7 +23,7 @@ class UserVerify extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { errorMessage, isPending, navigate } = this.props;
|
||||
const { errorMessage, isPending, navigate, verifyPhone, modal } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<section className="card card--form">
|
||||
|
@ -66,7 +66,30 @@ class UserVerify extends React.PureComponent {
|
|||
</section>
|
||||
<section className="card card--form">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('2) Proof via YouTube')}</h3>
|
||||
<h3>{__('2) Proof via Phone')}</h3>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
{`${__(
|
||||
'You will receive an SMS text message confirming that your phone number is correct.'
|
||||
)}`}
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
<Link
|
||||
onClick={() => {
|
||||
verifyPhone();
|
||||
}}
|
||||
button="alt"
|
||||
icon="icon-phone"
|
||||
label={__('Submit Phone Number')}
|
||||
/>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<div className="meta">{__('Standard messaging rates apply.')} </div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="card card--form">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('3) Proof via YouTube')}</h3>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<p>
|
||||
|
@ -96,7 +119,7 @@ class UserVerify extends React.PureComponent {
|
|||
</section>
|
||||
<section className="card card--form">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('3) Proof via Chat')}</h3>
|
||||
<h3>{__('4) Proof via Chat')}</h3>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<p>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Form, FormRow, Submit } from 'component/form';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { regexAddress } from 'lbryURI';
|
||||
|
||||
class WalletSend extends React.PureComponent {
|
||||
handleSubmit() {
|
||||
|
@ -42,7 +42,7 @@ class WalletSend extends React.PureComponent {
|
|||
size="60"
|
||||
onChange={setAddress}
|
||||
value={address}
|
||||
regexp={lbryuri.REGEXP_ADDRESS}
|
||||
regexp={regexAddress}
|
||||
trim
|
||||
/>
|
||||
<div className="form-row-submit">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import lbryuri from 'lbryuri.js';
|
||||
import { normalizeURI } from 'lbryURI.js';
|
||||
import { selectWunderBarAddress, selectWunderBarIcon } from 'redux/selectors/search';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import Wunderbar from './view';
|
||||
|
@ -13,7 +13,7 @@ const select = state => ({
|
|||
const perform = dispatch => ({
|
||||
onSearch: query => dispatch(doNavigate('/search', { query })),
|
||||
onSubmit: (query, extraParams) =>
|
||||
dispatch(doNavigate('/show', { uri: lbryuri.normalize(query), ...extraParams })),
|
||||
dispatch(doNavigate('/show', { uri: normalizeURI(query), ...extraParams })),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(Wunderbar);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import lbryuri from 'lbryuri.js';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import Icon from 'component/icon';
|
||||
import { parseQueryParams } from 'util/query_params';
|
||||
|
||||
|
@ -130,7 +130,7 @@ class WunderBar extends React.PureComponent {
|
|||
if (parts.length > 0) extraParams = parseQueryParams(parts.join(''));
|
||||
|
||||
try {
|
||||
uri = lbryuri.normalize(value);
|
||||
uri = normalizeURI(value);
|
||||
this.setState({ value: uri });
|
||||
} catch (error) {
|
||||
// then it's not a valid URL, so let's search
|
||||
|
|
|
@ -97,6 +97,7 @@ export const SEARCH_CANCELLED = 'SEARCH_CANCELLED';
|
|||
// Settings
|
||||
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
||||
export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
|
||||
export const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT';
|
||||
|
||||
// User
|
||||
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
|
||||
|
@ -110,6 +111,13 @@ export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE';
|
|||
export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED';
|
||||
export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS';
|
||||
export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE';
|
||||
export const USER_PHONE_RESET = 'USER_PHONE_RESET';
|
||||
export const USER_PHONE_NEW_STARTED = 'USER_PHONE_NEW_STARTED';
|
||||
export const USER_PHONE_NEW_SUCCESS = 'USER_PHONE_NEW_SUCCESS';
|
||||
export const USER_PHONE_NEW_FAILURE = 'USER_PHONE_NEW_FAILURE';
|
||||
export const USER_PHONE_VERIFY_STARTED = 'USER_PHONE_VERIFY_STARTED';
|
||||
export const USER_PHONE_VERIFY_SUCCESS = 'USER_PHONE_VERIFY_SUCCESS';
|
||||
export const USER_PHONE_VERIFY_FAILURE = 'USER_PHONE_VERIFY_FAILURE';
|
||||
export const USER_IDENTITY_VERIFY_STARTED = 'USER_IDENTITY_VERIFY_STARTED';
|
||||
export const USER_IDENTITY_VERIFY_SUCCESS = 'USER_IDENTITY_VERIFY_SUCCESS';
|
||||
export const USER_IDENTITY_VERIFY_FAILURE = 'USER_IDENTITY_VERIFY_FAILURE';
|
||||
|
|
|
@ -9,6 +9,7 @@ export const INSUFFICIENT_CREDITS = 'insufficient_credits';
|
|||
export const UPGRADE = 'upgrade';
|
||||
export const WELCOME = 'welcome';
|
||||
export const EMAIL_COLLECTION = 'email_collection';
|
||||
export const PHONE_COLLECTION = 'phone_collection';
|
||||
export const FIRST_REWARD = 'first_reward';
|
||||
export const AUTHENTICATION_FAILURE = 'auth_failure';
|
||||
export const TRANSACTION_FAILED = 'transaction_failed';
|
||||
|
|
|
@ -11,3 +11,4 @@ export const INSTANT_PURCHASE_ENABLED = 'instantPurchaseEnabled';
|
|||
export const INSTANT_PURCHASE_MAX = 'instantPurchaseMax';
|
||||
export const THEME = 'theme';
|
||||
export const THEMES = 'themes';
|
||||
export const AUTOMATIC_DARK_MODE_ENABLED = 'automaticDarkModeEnabled';
|
||||
|
|
|
@ -116,6 +116,7 @@ const init = () => {
|
|||
app.store.dispatch(doAutoUpdate());
|
||||
});
|
||||
}
|
||||
app.store.dispatch(doUpdateIsNightAsync());
|
||||
app.store.dispatch(doDownloadLanguages());
|
||||
|
||||
function onDaemonReady() {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
const CHANNEL_NAME_MIN_LEN = 1;
|
||||
const CLAIM_ID_MAX_LEN = 40;
|
||||
const channelNameMinLength = 1;
|
||||
const claimIdMaxLength = 40;
|
||||
|
||||
const Lbryuri = {};
|
||||
|
||||
Lbryuri.REGEXP_INVALID_URI = /[^A-Za-z0-9-]/g;
|
||||
Lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
||||
export const regexInvalidURI = /[^A-Za-z0-9-]/g;
|
||||
export const regexAddress = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
||||
|
||||
/**
|
||||
* Parses a LBRY name into its component parts. Throws errors with user-friendly
|
||||
|
@ -28,7 +26,7 @@ Lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
|||
* - contentName (string): For anon claims, the name; for channel claims, the path
|
||||
* - channelName (string, if present): Channel name without @
|
||||
*/
|
||||
Lbryuri.parse = (uri, requireProto = false) => {
|
||||
export function parseURI(URI, requireProto = false) {
|
||||
// Break into components. Empty sub-matches are converted to null
|
||||
const componentsRegex = new RegExp(
|
||||
'^((?:lbry://)?)' + // protocol
|
||||
|
@ -37,7 +35,7 @@ Lbryuri.parse = (uri, requireProto = false) => {
|
|||
'(/?)(.*)' // path separator, path
|
||||
);
|
||||
const [proto, name, modSep, modVal, pathSep, path] = componentsRegex
|
||||
.exec(uri)
|
||||
.exec(URI)
|
||||
.slice(1)
|
||||
.map(match => match || null);
|
||||
|
||||
|
@ -61,14 +59,14 @@ Lbryuri.parse = (uri, requireProto = false) => {
|
|||
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 < channelNameMinLength) {
|
||||
throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
||||
}
|
||||
|
||||
contentName = path;
|
||||
}
|
||||
|
||||
const nameBadChars = (channelName || name).match(Lbryuri.REGEXP_INVALID_URI);
|
||||
const nameBadChars = (channelName || name).match(regexInvalidURI);
|
||||
if (nameBadChars) {
|
||||
throw new Error(
|
||||
__(
|
||||
|
@ -99,7 +97,7 @@ Lbryuri.parse = (uri, requireProto = false) => {
|
|||
|
||||
if (
|
||||
claimId &&
|
||||
(claimId.length > CLAIM_ID_MAX_LEN || !claimId.match(/^[0-9a-f]+$/)) &&
|
||||
(claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/)) &&
|
||||
!claimId.match(/^pending/) // ought to be dropped when savePendingPublish drops hack
|
||||
) {
|
||||
throw new Error(__(`Invalid claim ID %s.`, claimId));
|
||||
|
@ -119,7 +117,7 @@ Lbryuri.parse = (uri, requireProto = false) => {
|
|||
throw new Error(__('Only channel URIs may have a path.'));
|
||||
}
|
||||
|
||||
const pathBadChars = path.match(Lbryuri.REGEXP_INVALID_URI);
|
||||
const pathBadChars = path.match(regexInvalidURI);
|
||||
if (pathBadChars) {
|
||||
throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', ')));
|
||||
}
|
||||
|
@ -140,17 +138,17 @@ Lbryuri.parse = (uri, requireProto = false) => {
|
|||
...(claimId ? { claimId } : {}),
|
||||
...(path ? { path } : {}),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an object in the same format returned by lbryuri.parse() and builds a URI.
|
||||
* Takes an object in the same format returned by parse() and builds a URI.
|
||||
*
|
||||
* The channelName key will accept names with or without the @ prefix.
|
||||
*/
|
||||
Lbryuri.build = (uriObj, includeProto = true) => {
|
||||
const { claimId, claimSequence, bidPosition, contentName, channelName } = uriObj;
|
||||
export function buildURI(URIObj, includeProto = true) {
|
||||
const { claimId, claimSequence, bidPosition, contentName, channelName } = URIObj;
|
||||
|
||||
let { name, path } = uriObj;
|
||||
let { name, path } = URIObj;
|
||||
|
||||
if (channelName) {
|
||||
const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`;
|
||||
|
@ -188,36 +186,35 @@ Lbryuri.build = (uriObj, includeProto = true) => {
|
|||
(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 = uri => {
|
||||
if (uri.match(/pending_claim/)) return uri;
|
||||
/* Takes a parseable LBRY URI and converts it to standard, canonical format */
|
||||
export function normalizeURI(URI) {
|
||||
if (URI.match(/pending_claim/)) return URI;
|
||||
|
||||
const { name, path, bidPosition, claimSequence, claimId } = Lbryuri.parse(uri);
|
||||
return Lbryuri.build({ name, path, claimSequence, bidPosition, claimId });
|
||||
};
|
||||
const { name, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
||||
return buildURI({ name, path, claimSequence, bidPosition, claimId });
|
||||
}
|
||||
|
||||
Lbryuri.isValid = uri => {
|
||||
export function isURIValid(URI) {
|
||||
let parts;
|
||||
try {
|
||||
parts = Lbryuri.parse(Lbryuri.normalize(uri));
|
||||
parts = parseURI(normalizeURI(URI));
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return parts && parts.name;
|
||||
};
|
||||
}
|
||||
|
||||
Lbryuri.isValidName = (name, checkCase = true) => {
|
||||
export function isNameValid(name, checkCase = true) {
|
||||
const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
|
||||
return regexp.test(name);
|
||||
};
|
||||
}
|
||||
|
||||
Lbryuri.isClaimable = uri => {
|
||||
export function isURIClaimable(URI) {
|
||||
let parts;
|
||||
try {
|
||||
parts = Lbryuri.parse(Lbryuri.normalize(uri));
|
||||
parts = parseURI(normalizeURI(URI));
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
@ -230,7 +227,4 @@ Lbryuri.isClaimable = uri => {
|
|||
!parts.isChannel &&
|
||||
!parts.path
|
||||
);
|
||||
};
|
||||
|
||||
window.lbryuri = Lbryuri;
|
||||
export default Lbryuri;
|
||||
}
|
22
src/renderer/modal/modalPhoneCollection/index.js
Normal file
22
src/renderer/modal/modalPhoneCollection/index.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import * as settings from 'constants/settings';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import { selectPhoneToVerify, selectUser } from 'redux/selectors/user';
|
||||
import ModalPhoneCollection from './view';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
|
||||
const select = state => ({
|
||||
phone: selectPhoneToVerify(state),
|
||||
user: selectUser(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => () => ({
|
||||
closeModal: () => {
|
||||
dispatch(doCloseModal());
|
||||
dispatch(doNavigate('/rewards'));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(select, perform)(ModalPhoneCollection);
|
40
src/renderer/modal/modalPhoneCollection/view.jsx
Normal file
40
src/renderer/modal/modalPhoneCollection/view.jsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import React from 'react';
|
||||
import { Modal } from 'modal/modal';
|
||||
import Link from 'component/link/index';
|
||||
import UserPhoneNew from 'component/userPhoneNew';
|
||||
import UserPhoneVerify from 'component/userPhoneVerify';
|
||||
|
||||
class ModalPhoneCollection extends React.PureComponent {
|
||||
renderInner() {
|
||||
const { closeModal, phone, user } = this.props;
|
||||
|
||||
const cancelButton = <Link button="text" onClick={closeModal} label={__('Not Now')} />;
|
||||
|
||||
if (!user.phone_number && !phone) {
|
||||
return <UserPhoneNew cancelButton={cancelButton} />;
|
||||
} else if (!user.phone_number) {
|
||||
return <UserPhoneVerify cancelButton={cancelButton} />;
|
||||
}
|
||||
closeModal();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { user } = this.props;
|
||||
|
||||
// this shouldn't happen
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal type="custom" isOpen contentLabel="Phone">
|
||||
<section>
|
||||
<h3 className="modal__header">Verify Your Phone</h3>
|
||||
{this.renderInner()}
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalPhoneCollection;
|
|
@ -15,6 +15,7 @@ import ModalFileTimeout from 'modal/modalFileTimeout';
|
|||
import ModalAffirmPurchase from 'modal/modalAffirmPurchase';
|
||||
import ModalRevokeClaim from 'modal/modalRevokeClaim';
|
||||
import ModalEmailCollection from '../modalEmailCollection';
|
||||
import ModalPhoneCollection from '../modalPhoneCollection';
|
||||
import * as modals from 'constants/modal_types';
|
||||
|
||||
class ModalRouter extends React.PureComponent {
|
||||
|
@ -130,6 +131,8 @@ class ModalRouter extends React.PureComponent {
|
|||
return <ModalAffirmPurchase {...modalProps} />;
|
||||
case modals.CONFIRM_CLAIM_REVOKE:
|
||||
return <ModalRevokeClaim {...modalProps} />;
|
||||
case modals.PHONE_COLLECTION:
|
||||
return <ModalPhoneCollection {...modalProps} />;
|
||||
case modals.EMAIL_COLLECTION:
|
||||
return <ModalEmailCollection {...modalProps} />;
|
||||
default:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import FileTile from 'component/fileTile';
|
||||
import ReactPaginate from 'react-paginate';
|
||||
|
@ -46,7 +46,7 @@ class ChannelPage extends React.PureComponent {
|
|||
} = this.props;
|
||||
|
||||
const { name, claim_id: claimId } = claim;
|
||||
const subscriptionUri = lbryuri.build({ channelName: name, claimId }, false);
|
||||
const subscriptionUri = buildURI({ channelName: name, claimId }, false);
|
||||
|
||||
let contentList;
|
||||
if (fetching) {
|
||||
|
@ -57,7 +57,7 @@ class ChannelPage extends React.PureComponent {
|
|||
claimsInChannel.map(claim => (
|
||||
<FileTile
|
||||
key={claim.claim_id}
|
||||
uri={lbryuri.build({
|
||||
uri={buildURI({
|
||||
name: claim.name,
|
||||
claimId: claim.claim_id,
|
||||
})}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import FileCard from 'component/fileCard';
|
||||
import { BusyMessage } from 'component/common.js';
|
||||
import Icon from 'component/icon';
|
||||
|
@ -212,7 +212,7 @@ export class FeaturedCategory extends React.PureComponent {
|
|||
<div ref="rowitems" className="card-row__items">
|
||||
{names &&
|
||||
names.map(name => (
|
||||
<FileCard key={name} displayStyle="card" uri={lbryuri.normalize(name)} />
|
||||
<FileCard key={name} displayStyle="card" uri={normalizeURI(name)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { buildURI, normalizeURI } from 'lbryURI';
|
||||
import Video from 'component/video';
|
||||
import { Thumbnail } from 'component/common';
|
||||
import FilePrice from 'component/filePrice';
|
||||
|
@ -65,7 +65,7 @@ class FilePage extends React.PureComponent {
|
|||
|
||||
let subscriptionUri;
|
||||
if (channelName && channelClaimId) {
|
||||
subscriptionUri = lbryuri.build({ channelName, claimId: channelClaimId }, false);
|
||||
subscriptionUri = buildURI({ channelName, claimId: channelClaimId }, false);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -86,7 +86,7 @@ class FilePage extends React.PureComponent {
|
|||
<div className="card__title-identity">
|
||||
{!fileInfo || fileInfo.written_bytes <= 0 ? (
|
||||
<span style={{ float: 'right' }}>
|
||||
<FilePrice uri={lbryuri.normalize(uri)} />
|
||||
<FilePrice uri={normalizeURI(uri)} />
|
||||
{isRewardContent && (
|
||||
<span>
|
||||
{' '}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { isURIValid, normalizeURI } from 'lbryURI';
|
||||
import FileTile from 'component/fileTile';
|
||||
import FileListSearch from 'component/fileListSearch';
|
||||
import { ToolTip } from 'component/tooltip.js';
|
||||
|
@ -10,7 +10,7 @@ class SearchPage extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
{lbryuri.isValid(query) ? (
|
||||
{isURIValid(query) ? (
|
||||
<section className="section-spaced">
|
||||
<h3 className="card-row__header">
|
||||
{__('Exact URL')}{' '}
|
||||
|
@ -20,7 +20,7 @@ class SearchPage extends React.PureComponent {
|
|||
className="tooltip--header"
|
||||
/>
|
||||
</h3>
|
||||
<FileTile uri={lbryuri.normalize(query)} showEmpty={FileTile.SHOW_EMPTY_PUBLISH} />
|
||||
<FileTile uri={normalizeURI(query)} showEmpty={FileTile.SHOW_EMPTY_PUBLISH} />
|
||||
</section>
|
||||
) : (
|
||||
''
|
||||
|
|
|
@ -28,6 +28,7 @@ const select = state => ({
|
|||
themes: makeSelectClientSetting(settings.THEMES)(state),
|
||||
language: selectCurrentLanguage(state),
|
||||
languages: selectLanguages(state),
|
||||
automaticDarkModeEnabled: makeSelectClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED)(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
|
|
|
@ -65,6 +65,10 @@ class SettingsPage extends React.PureComponent {
|
|||
this.props.setClientSetting(settings.THEME, value);
|
||||
}
|
||||
|
||||
onAutomaticDarkModeChange(event) {
|
||||
this.props.setClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED, event.target.checked);
|
||||
}
|
||||
|
||||
onInstantPurchaseEnabledChange(enabled) {
|
||||
this.props.setClientSetting(settings.INSTANT_PURCHASE_ENABLED, enabled);
|
||||
}
|
||||
|
@ -129,6 +133,7 @@ class SettingsPage extends React.PureComponent {
|
|||
showUnavailable,
|
||||
theme,
|
||||
themes,
|
||||
automaticDarkModeEnabled,
|
||||
} = this.props;
|
||||
|
||||
if (!daemonSettings || Object.keys(daemonSettings).length === 0) {
|
||||
|
@ -317,6 +322,13 @@ class SettingsPage extends React.PureComponent {
|
|||
</option>
|
||||
))}
|
||||
</FormField>
|
||||
|
||||
<FormRow
|
||||
type="checkbox"
|
||||
onChange={this.onAutomaticDarkModeChange.bind(this)}
|
||||
defaultChecked={automaticDarkModeEnabled}
|
||||
label={__('Automatic dark mode (9pm to 8am)')}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import ChannelPage from 'page/channel';
|
||||
import FilePage from 'page/file';
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as SETTINGS from 'constants/settings';
|
|||
import { ipcRenderer } from 'electron';
|
||||
import Lbry from 'lbry';
|
||||
import Lbryio from 'lbryio';
|
||||
import Lbryuri from 'lbryuri';
|
||||
import { normalizeURI, buildURI } from 'lbryURI';
|
||||
import { doAlertError, doOpenModal } from 'redux/actions/app';
|
||||
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
|
||||
import { selectBadgeNumber } from 'redux/selectors/app';
|
||||
|
@ -26,7 +26,7 @@ const DOWNLOAD_POLL_INTERVAL = 250;
|
|||
|
||||
export function doResolveUris(uris) {
|
||||
return (dispatch, getState) => {
|
||||
const normalizedUris = uris.map(Lbryuri.normalize);
|
||||
const normalizedUris = uris.map(normalizeURI);
|
||||
const state = getState();
|
||||
|
||||
// Filter out URIs that are already resolving
|
||||
|
@ -506,7 +506,7 @@ export function doAbandonClaim(txid, nout) {
|
|||
claimId,
|
||||
},
|
||||
});
|
||||
dispatch(doResolveUri(Lbryuri.build({ name, claimId })));
|
||||
dispatch(doResolveUri(buildURI({ name, claimId })));
|
||||
dispatch(doFetchClaimListMine());
|
||||
} else {
|
||||
dispatch(doOpenModal(MODALS.TRANSACTION_FAILED));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import Lbryuri from 'lbryuri';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import { doResolveUri } from 'redux/actions/content';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { selectCurrentPage } from 'redux/selectors/navigation';
|
||||
|
@ -40,7 +40,7 @@ export function doSearch(rawQuery) {
|
|||
const actions = [];
|
||||
|
||||
data.forEach(result => {
|
||||
const uri = Lbryuri.build({
|
||||
const uri = buildURI({
|
||||
name: result.name,
|
||||
claimId: result.claimId,
|
||||
});
|
||||
|
|
|
@ -4,6 +4,9 @@ import Fs from 'fs';
|
|||
import Http from 'http';
|
||||
|
||||
import Lbry from 'lbry';
|
||||
import moment from 'moment';
|
||||
|
||||
const UPDATE_IS_NIGHT_INTERVAL = 10 * 60 * 1000;
|
||||
|
||||
export function doFetchDaemonSettings() {
|
||||
return dispatch => {
|
||||
|
@ -51,6 +54,29 @@ export function doGetThemes() {
|
|||
};
|
||||
}
|
||||
|
||||
export function doUpdateIsNightAsync() {
|
||||
return dispatch => {
|
||||
dispatch(doUpdateIsNight());
|
||||
const updateIsNightInterval = setInterval(
|
||||
() => dispatch(doUpdateIsNight()),
|
||||
UPDATE_IS_NIGHT_INTERVAL
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function doUpdateIsNight() {
|
||||
const momentNow = moment();
|
||||
return {
|
||||
type: ACTIONS.UPDATE_IS_NIGHT,
|
||||
data: { isNight: () => {
|
||||
const startNightMoment = moment('19:00', 'HH:mm');
|
||||
const endNightMoment = moment('8:00', 'HH:mm');
|
||||
return !(momentNow.isAfter(endNightMoment) && momentNow.isBefore(startNightMoment));
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function doDownloadLanguage(langFile) {
|
||||
return dispatch => {
|
||||
const destinationPath = `${app.i18n.directory}/${langFile}`;
|
||||
|
|
|
@ -3,7 +3,11 @@ import * as MODALS from 'constants/modal_types';
|
|||
import Lbryio from 'lbryio';
|
||||
import { doOpenModal, doShowSnackBar } from 'redux/actions/app';
|
||||
import { doClaimRewardType, doRewardList } from 'redux/actions/rewards';
|
||||
import { selectEmailToVerify } from 'redux/selectors/user';
|
||||
import {
|
||||
selectEmailToVerify,
|
||||
selectPhoneToVerify,
|
||||
selectUserCountryCode,
|
||||
} from 'redux/selectors/user';
|
||||
import rewards from 'rewards';
|
||||
|
||||
export function doFetchInviteStatus() {
|
||||
|
@ -78,6 +82,78 @@ export function doUserFetch() {
|
|||
};
|
||||
}
|
||||
|
||||
export function doUserPhoneReset() {
|
||||
return {
|
||||
type: ACTIONS.USER_PHONE_RESET,
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserPhoneNew(phone, country_code) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_PHONE_NEW_STARTED,
|
||||
data: { phone, country_code },
|
||||
});
|
||||
|
||||
const success = () => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_PHONE_NEW_SUCCESS,
|
||||
data: { phone },
|
||||
});
|
||||
};
|
||||
|
||||
const failure = () => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_PHONE_NEW_FAILURE,
|
||||
data: { error: 'An error occurred while processing this phone number.' },
|
||||
});
|
||||
};
|
||||
|
||||
Lbryio.call('user', 'phone_number_new', { phone_number: phone, country_code }, 'post').then(
|
||||
success,
|
||||
failure
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserPhoneVerifyFailure(error) {
|
||||
return {
|
||||
type: ACTIONS.USER_PHONE_VERIFY_FAILURE,
|
||||
data: { error },
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserPhoneVerify(verificationCode) {
|
||||
return (dispatch, getState) => {
|
||||
const phoneNumber = selectPhoneToVerify(getState());
|
||||
const countryCode = selectUserCountryCode(getState());
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.USER_PHONE_VERIFY_STARTED,
|
||||
code: verificationCode,
|
||||
});
|
||||
|
||||
Lbryio.call(
|
||||
'user',
|
||||
'phone_number_confirm',
|
||||
{
|
||||
verification_code: verificationCode,
|
||||
phone_number: phoneNumber,
|
||||
country_code: countryCode,
|
||||
},
|
||||
'post'
|
||||
)
|
||||
.then(() => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_PHONE_VERIFY_SUCCESS,
|
||||
data: { phone_number: phoneNumber },
|
||||
});
|
||||
dispatch(doUserFetch());
|
||||
})
|
||||
.catch(error => dispatch(doUserPhoneVerifyFailure(error)));
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserEmailNew(email) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
|
|
|
@ -23,7 +23,9 @@ const defaultState = {
|
|||
language: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'),
|
||||
theme: getLocalStorageSetting(SETTINGS.THEME, 'light'),
|
||||
themes: getLocalStorageSetting(SETTINGS.THEMES, []),
|
||||
automaticDarkModeEnabled: getLocalStorageSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED, false),
|
||||
},
|
||||
isNight: false,
|
||||
languages: {},
|
||||
};
|
||||
|
||||
|
@ -46,6 +48,11 @@ reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
|
|||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.UPDATE_IS_NIGHT] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
isNight: action.data.isNight,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.DOWNLOAD_LANGUAGE_SUCCEEDED] = (state, action) => {
|
||||
const languages = Object.assign({}, state.languages);
|
||||
const { language } = action.data;
|
||||
|
|
|
@ -55,6 +55,55 @@ reducers[ACTIONS.USER_FETCH_FAILURE] = state =>
|
|||
user: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_NEW_STARTED] = (state, action) => {
|
||||
const user = Object.assign({}, state.user);
|
||||
user.country_code = action.data.country_code;
|
||||
return Object.assign({}, state, {
|
||||
phoneNewIsPending: true,
|
||||
phoneNewErrorMessage: '',
|
||||
user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
phoneToVerify: action.data.phone,
|
||||
phoneNewIsPending: false,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_RESET] = state =>
|
||||
Object.assign({}, state, {
|
||||
phoneToVerify: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_NEW_FAILURE] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
phoneNewIsPending: false,
|
||||
phoneNewErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_VERIFY_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
phoneVerifyIsPending: true,
|
||||
phoneVerifyErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_VERIFY_SUCCESS] = (state, action) => {
|
||||
const user = Object.assign({}, state.user);
|
||||
user.phone_number = action.data.phone_number;
|
||||
return Object.assign({}, state, {
|
||||
phoneToVerify: '',
|
||||
phoneVerifyIsPending: false,
|
||||
user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_VERIFY_FAILURE] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
phoneVerifyIsPending: false,
|
||||
phoneVerifyErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
emailNewIsPending: true,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Lbryuri from 'lbryuri';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { makeSelectCurrentParam } from 'redux/selectors/navigation';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
|
@ -32,7 +32,7 @@ export const selectAllClaimsByChannel = createSelector(
|
|||
);
|
||||
|
||||
export const makeSelectClaimForUri = uri =>
|
||||
createSelector(selectClaimsByUri, claims => claims && claims[Lbryuri.normalize(uri)]);
|
||||
createSelector(selectClaimsByUri, claims => claims && claims[normalizeURI(uri)]);
|
||||
|
||||
export const selectMyClaimsRaw = createSelector(selectState, state => state.myClaims);
|
||||
|
||||
|
@ -53,7 +53,7 @@ export const selectMyActiveClaims = createSelector(
|
|||
);
|
||||
|
||||
export const makeSelectClaimIsMine = rawUri => {
|
||||
const uri = Lbryuri.normalize(rawUri);
|
||||
const uri = normalizeURI(rawUri);
|
||||
return createSelector(
|
||||
selectClaimsByUri,
|
||||
selectMyActiveClaims,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import * as settings from 'constants/settings';
|
||||
import { createSelector } from 'reselect';
|
||||
import lbryuri from 'lbryuri';
|
||||
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||
|
||||
const _selectState = state => state.media || {};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import { parseQueryParams, toQueryString } from 'util/query_params';
|
||||
import Lbryuri from 'lbryuri';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
|
||||
export const selectState = state => state.navigation || {};
|
||||
|
||||
|
@ -93,7 +93,7 @@ export const selectPageTitle = createSelector(
|
|||
case 'developer':
|
||||
return __('Developer');
|
||||
case 'show': {
|
||||
const parts = [Lbryuri.normalize(params.uri)];
|
||||
const parts = [normalizeURI(params.uri)];
|
||||
// If the params has any keys other than "uri"
|
||||
if (Object.keys(params).length > 1) {
|
||||
parts.push(toQueryString(Object.assign({}, params, { uri: null })));
|
||||
|
|
|
@ -18,7 +18,18 @@ export const selectShowNsfw = makeSelectClientSetting(SETTINGS.SHOW_NSFW);
|
|||
|
||||
export const selectLanguages = createSelector(selectState, state => state.languages || {});
|
||||
|
||||
export const selectThemePath = createSelector(
|
||||
makeSelectClientSetting(SETTINGS.THEME),
|
||||
theme => `${staticResourcesPath}/themes/${theme || 'light'}.css`
|
||||
export const selectTheme = makeSelectClientSetting(SETTINGS.THEME);
|
||||
export const selectAutomaticDarkModeEnabled = makeSelectClientSetting(
|
||||
SETTINGS.AUTOMATIC_DARK_MODE_ENABLED
|
||||
);
|
||||
export const selectIsNight = createSelector(selectState, state => state.isNight);
|
||||
|
||||
export const selectThemePath = createSelector(
|
||||
selectTheme,
|
||||
selectAutomaticDarkModeEnabled,
|
||||
selectIsNight,
|
||||
(theme, automaticDarkModeEnabled, isNight) => {
|
||||
const dynamicTheme = automaticDarkModeEnabled && isNight ? 'dark' : theme;
|
||||
return `${staticResourcesPath}/themes/${dynamicTheme || 'light'}.css`;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -16,12 +16,28 @@ export const selectUserEmail = createSelector(
|
|||
user => (user ? user.primary_email : null)
|
||||
);
|
||||
|
||||
export const selectUserPhone = createSelector(
|
||||
selectUser,
|
||||
user => (user ? user.phone_number : null)
|
||||
);
|
||||
|
||||
export const selectUserCountryCode = createSelector(
|
||||
selectUser,
|
||||
user => (user ? user.country_code : null)
|
||||
);
|
||||
|
||||
export const selectEmailToVerify = createSelector(
|
||||
selectState,
|
||||
selectUserEmail,
|
||||
(state, userEmail) => state.emailToVerify || userEmail
|
||||
);
|
||||
|
||||
export const selectPhoneToVerify = createSelector(
|
||||
selectState,
|
||||
selectUserPhone,
|
||||
(state, userPhone) => state.phoneToVerify || userPhone
|
||||
);
|
||||
|
||||
export const selectUserIsRewardApproved = createSelector(
|
||||
selectUser,
|
||||
user => user && user.is_reward_approved
|
||||
|
@ -37,6 +53,11 @@ export const selectEmailNewErrorMessage = createSelector(
|
|||
state => state.emailNewErrorMessage
|
||||
);
|
||||
|
||||
export const selectPhoneNewErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.phoneNewErrorMessage
|
||||
);
|
||||
|
||||
export const selectEmailVerifyIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.emailVerifyIsPending
|
||||
|
@ -47,6 +68,11 @@ export const selectEmailVerifyErrorMessage = createSelector(
|
|||
state => state.emailVerifyErrorMessage
|
||||
);
|
||||
|
||||
export const selectPhoneVerifyErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.phoneVerifyErrorMessage
|
||||
);
|
||||
|
||||
export const selectIdentityVerifyIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.identityVerifyIsPending
|
||||
|
|
|
@ -79,6 +79,7 @@ $text-color: #000;
|
|||
/* Select */
|
||||
--select-bg: var(--color-bg-alt);
|
||||
--select-color: var(--text-color);
|
||||
--select-height: 30px;
|
||||
|
||||
/* Button */
|
||||
--button-bg: var(--color-bg-alt);
|
||||
|
|
|
@ -5,6 +5,15 @@
|
|||
margin-bottom: $spacing-vertical;
|
||||
}
|
||||
|
||||
.form-row-phone {
|
||||
display: flex;
|
||||
|
||||
.form-field__input-text {
|
||||
margin-left: 5px;
|
||||
width: calc(0.85 * var(--input-width));
|
||||
}
|
||||
}
|
||||
|
||||
.form-row__label-row {
|
||||
margin-top: $spacing-vertical * 5/6;
|
||||
margin-bottom: 0px;
|
||||
|
@ -32,7 +41,7 @@
|
|||
box-sizing: border-box;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
height: $spacing-vertical;
|
||||
height: var(--select-height);
|
||||
background: var(--select-bg);
|
||||
color: var(--select-color);
|
||||
&:focus {
|
||||
|
|
19
yarn.lock
19
yarn.lock
|
@ -2213,6 +2213,13 @@ cosmiconfig@^3.1.0:
|
|||
parse-json "^3.0.0"
|
||||
require-from-string "^2.0.1"
|
||||
|
||||
country-data@^0.0.31:
|
||||
version "0.0.31"
|
||||
resolved "https://registry.yarnpkg.com/country-data/-/country-data-0.0.31.tgz#80966b8e1d147fa6d6a589d32933f8793774956d"
|
||||
dependencies:
|
||||
currency-symbol-map "~2"
|
||||
underscore ">1.4.4"
|
||||
|
||||
create-ecdh@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d"
|
||||
|
@ -2406,6 +2413,10 @@ csso@~2.3.1:
|
|||
clap "^1.0.9"
|
||||
source-map "^0.5.3"
|
||||
|
||||
currency-symbol-map@~2:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/currency-symbol-map/-/currency-symbol-map-2.2.0.tgz#2b3c1872ff1ac2ce595d8273e58e1fff0272aea2"
|
||||
|
||||
currently-unhandled@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
|
||||
|
@ -5769,6 +5780,10 @@ modify-filename@^1.1.0:
|
|||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1"
|
||||
|
||||
moment@^2.20.1:
|
||||
version "2.20.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd"
|
||||
|
||||
move-concurrently@^1.0.1, move-concurrently@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
||||
|
@ -8812,6 +8827,10 @@ unc-path-regex@^0.1.0:
|
|||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
|
||||
|
||||
underscore@>1.4.4:
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
|
||||
|
||||
uniq@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
|
||||
|
|
Loading…
Add table
Reference in a new issue