Skip to content
Snippets Groups Projects
Commit 4f1099bc authored by valzav's avatar valzav
Browse files

Merge branch 'master' into new-url-scheme

parents 456af645 fd7915ce
No related branches found
No related tags found
No related merge requests found
Showing
with 248 additions and 90 deletions
import '@storybook/addon-knobs/register'; import '@storybook/addon-knobs/register';
\ No newline at end of file import 'storybook-addon-intl/register';
\ No newline at end of file
import { configure } from '@storybook/react'; import { addDecorator, configure } from '@storybook/react';
import { setIntlConfig, withIntl } from 'storybook-addon-intl';
import tt from 'counterpart';
import { addLocaleData } from 'react-intl';
import en from 'react-intl/locale-data/en';
import es from 'react-intl/locale-data/es';
import ru from 'react-intl/locale-data/ru';
import fr from 'react-intl/locale-data/fr';
import it from 'react-intl/locale-data/it';
import ko from 'react-intl/locale-data/ko';
import zh from 'react-intl/locale-data/zh';
import pl from 'react-intl/locale-data/pl';
addLocaleData([...en, ...es, ...ru, ...fr, ...it, ...ko, ...zh, ...pl]);
tt.registerTranslations('en', require('counterpart/locales/en'));
tt.registerTranslations('en', require('app/locales/en.json'));
tt.registerTranslations('es', require('app/locales/counterpart/es'));
tt.registerTranslations('es', require('app/locales/es.json'));
tt.registerTranslations('ru', require('counterpart/locales/ru'));
tt.registerTranslations('ru', require('app/locales/ru.json'));
tt.registerTranslations('fr', require('app/locales/counterpart/fr'));
tt.registerTranslations('fr', require('app/locales/fr.json'));
tt.registerTranslations('it', require('app/locales/counterpart/it'));
tt.registerTranslations('it', require('app/locales/it.json'));
tt.registerTranslations('ko', require('app/locales/counterpart/ko'));
tt.registerTranslations('ko', require('app/locales/ko.json'));
tt.registerTranslations('zh', require('app/locales/counterpart/zh'));
tt.registerTranslations('zh', require('app/locales/zh.json'));
tt.registerTranslations('pl', require('app/locales/counterpart/pl'));
tt.registerTranslations('pl', require('app/locales/pl.json'));
const getMessages = (locale) => {
tt.setLocale(locale)
return tt('g')
}
setIntlConfig({
locales: ['en', 'es', 'ru', 'fr', 'it', 'ko', 'zh', 'pl'],
defaultLocale: 'en',
getMessages
});
addDecorator(withIntl);
const req = require.context('../src/app/components', true, /story\.jsx$/); const req = require.context('../src/app/components', true, /story\.jsx$/);
...@@ -6,5 +56,4 @@ function loadStories() { ...@@ -6,5 +56,4 @@ function loadStories() {
req.keys().forEach(req); req.keys().forEach(req);
} }
configure(loadStories, module); configure(loadStories, module);
...@@ -2,10 +2,16 @@ ...@@ -2,10 +2,16 @@
# Condenser # Condenser
Condenser is the react.js web interface to the world's first and best blockchain-based social media platform, steemit.com. It uses [STEEM](https://github.com/steemit/steem), a blockchain powered by Graphene 2.0 technology to store JSON-based content for a plethora of web applications. Condenser is the react.js web interface to the world's first and best
blockchain-based social media platform, steemit.com. It uses
[STEEM](https://github.com/steemit/steem), a blockchain powered by Graphene
2.0 technology to store JSON-based content for a plethora of web
applications.
## Why would I want to use Condenser (steemit.com front-end)? ## Why would I want to use Condenser (steemit.com front-end)?
* Learning how to build blockchain-based web applications using STEEM as a content storage mechanism in react.js
* Learning how to build blockchain-based web applications using STEEM as a
content storage mechanism in react.js
* Reviewing the inner workings of the steemit.com social media platform * Reviewing the inner workings of the steemit.com social media platform
* Assisting with software development for steemit.com * Assisting with software development for steemit.com
...@@ -13,7 +19,13 @@ Condenser is the react.js web interface to the world's first and best blockchain ...@@ -13,7 +19,13 @@ Condenser is the react.js web interface to the world's first and best blockchain
#### Docker #### Docker
We highly recommend using docker to run condenser. This is how we run the live steemit.com site and it is the most supported (and fastest) method of both building and running condenser. We will always have the latest version of condenser (master branch) available on dockerhub. Configuration settings can be set using environment variables (see configuration section below for more information). If you need to install docker, you can get it at https://get.docker.com We highly recommend using docker to run condenser. This is how we run the
live steemit.com site and it is the most supported (and fastest) method of
both building and running condenser. We will always have the latest version
of condenser (master branch) available on Docker Hub. Configuration settings
can be set using environment variables (see configuration section below for
more information). If you need to install docker, you can get it at
https://get.docker.com
To bring up a running container it's as simple as this: To bring up a running container it's as simple as this:
...@@ -27,7 +39,9 @@ Environment variables can be added like this: ...@@ -27,7 +39,9 @@ Environment variables can be added like this:
docker run -it --env SDC_DATABASE_URL="mysql://user:pass@hostname/databasename" -p 8080:8080 steemit/condenser docker run -it --env SDC_DATABASE_URL="mysql://user:pass@hostname/databasename" -p 8080:8080 steemit/condenser
``` ```
If you would like to modify, build, and run condenser using docker, it's as simple as pulling in the github repo and issuing one command to build it, like this: If you would like to modify, build, and run condenser using docker, it's as
simple as pulling in the github repo and issuing one command to build it,
like this:
```bash ```bash
git clone https://github.com/steemit/condenser git clone https://github.com/steemit/condenser
...@@ -39,6 +53,7 @@ docker run -it -p 8080:8080 myname/condenser:mybranch ...@@ -39,6 +53,7 @@ docker run -it -p 8080:8080 myname/condenser:mybranch
## Building from source without docker (the 'traditional' way): ## Building from source without docker (the 'traditional' way):
#### Clone the repository and make a tmp folder #### Clone the repository and make a tmp folder
```bash ```bash
git clone https://github.com/steemit/condenser git clone https://github.com/steemit/condenser
cd condenser cd condenser
...@@ -47,21 +62,32 @@ mkdir tmp ...@@ -47,21 +62,32 @@ mkdir tmp
#### Install dependencies #### Install dependencies
Install at least Node v8.7 if you don't already have it. We recommend using `nvm` to do this as it's both the simplest way to install and manage installed version(s) of node. If you need `nvm`, you can get it at [https://github.com/creationix/nvm](https://github.com/creationix/nvm). Install at least Node v8.7 if you don't already have it. We recommend using
`nvm` to do this as it's both the simplest way to install and manage
installed version(s) of node. If you need `nvm`, you can get it at
[https://github.com/creationix/nvm](https://github.com/creationix/nvm).
Condenser is known to successfully build using node 8.7, npm 5.4.2, and yarn 1.3.2. Condenser is known to successfully build using node 8.7, npm 5.4.2, and
yarn 1.3.2.
Using nvm, you would install like this: Using nvm, you would install like this:
```bash ```bash
nvm install v8.7 nvm install v8.7
``` ```
We use the yarn package manager instead of the default `npm`. There are multiple reasons for this, one being that we have `steem-js` built from source pulling the github repo as part of the build process and yarn supports this. This way the library that handles keys can be loaded by commit hash instead of a version name and cryptographically verified to be exactly what we expect it to be. Yarn can be installed with `npm`, but afterwards you will not need to use `npm` further. We use the yarn package manager instead of the default `npm`. There are
multiple reasons for this, one being that we have `steem-js` built from
source pulling the github repo as part of the build process and yarn
supports this. This way the library that handles keys can be loaded by
commit hash instead of a version name and cryptographically verified to be
exactly what we expect it to be. Yarn can be installed with `npm`, but
afterwards you will not need to use `npm` further.
```bash ```bash
npm install -g yarn npm install -g yarn
yarn global add babel-cli yarn global add babel-cli
yarn install yarn install --frozen-lockfile
yarn run build yarn run build
``` ```
To run condenser in production mode, run: To run condenser in production mode, run:
...@@ -70,7 +96,9 @@ To run condenser in production mode, run: ...@@ -70,7 +96,9 @@ To run condenser in production mode, run:
yarn run production yarn run production
``` ```
When launching condenser in production mode it will automatically use 1 process per available core. You will be able to access the front-end at http://localhost:8080 by default. When launching condenser in production mode it will automatically use 1
process per available core. You will be able to access the front-end at
http://localhost:8080 by default.
To run condenser in development mode, run: To run condenser in development mode, run:
...@@ -78,13 +106,23 @@ To run condenser in development mode, run: ...@@ -78,13 +106,23 @@ To run condenser in development mode, run:
yarn run start yarn run start
``` ```
It will take quite a bit longer to start in this mode (~60s) as it needs to build and start the webpack-dev-server. It will take quite a bit longer to start in this mode (~60s) as it needs to
build and start the webpack-dev-server.
By default you will be connected to steemit.com's public steem node at `wss://steemd.steeemit.com`. This is actually on the real blockchain and you would use your regular account name and credentials to login - there is not an official separate testnet at this time. If you intend to run a full-fledged site relying on your own, we recommend looking into running a copy of `steemd` locally instead [https://github.com/steemit/steem](https://github.com/steemit/steem). By default you will be connected to steemit.com's public steem node at
`wss://steemd.steeemit.com`. This is actually on the real blockchain and
you would use your regular account name and credentials to login - there is
not an official separate testnet at this time. If you intend to run a
full-fledged site relying on your own, we recommend looking into running a
copy of `steemd` locally instead
[https://github.com/steemit/steem](https://github.com/steemit/steem).
#### Configuration #### Configuration
The intention is to configure condenser using environment variables. You can see the names of all of the available configuration environment variables in `config/custom-environment-variables.json`. Default values are stored in `config/defaults.json`. The intention is to configure condenser using environment variables. You
can see the names of all of the available configuration environment
variables in `config/custom-environment-variables.json`. Default values are
stored in `config/defaults.json`.
Environment variables using an example like this: Environment variables using an example like this:
...@@ -92,11 +130,22 @@ Environment variables using an example like this: ...@@ -92,11 +130,22 @@ Environment variables using an example like this:
export SDC_CLIENT_STEEMD_URL="wss://steemd.steemit.com" export SDC_CLIENT_STEEMD_URL="wss://steemd.steemit.com"
export SDC_SERVER_STEEMD_URL="wss://steemd.steemit.com" export SDC_SERVER_STEEMD_URL="wss://steemd.steemit.com"
``` ```
Keep in mind environment variables only exist in your active session, so if you wish to save them for later use you can put them all in a file and `source` them in.
If you'd like to statically configure condenser without variables you can edit the settings directly in `config/production.json`. If you're running in development mode, copy `config/production.json` to `config/dev.json` with `cp config/production.json config/dev.json` and adjust settings in `dev.json`. Keep in mind environment variables only exist in your active session, so if
you wish to save them for later use you can put them all in a file and
`source` them in.
If you'd like to statically configure condenser without variables you can
edit the settings directly in `config/production.json`. If you're running
in development mode, copy `config/production.json` to `config/dev.json`
with `cp config/production.json config/dev.json` and adjust settings in
`dev.json`.
If you're intending to run condenser in a production environment one configuration option that you will definitely want to edit is `server_session_secret` which can be set by the environment variable `SDC_SESSION_SECRETKEY`. To generate a new value for this setting, you can do this: If you're intending to run condenser in a production environment one
configuration option that you will definitely want to edit is
`server_session_secret` which can be set by the environment variable
`SDC_SESSION_SECRETKEY`. To generate a new value for this setting, you can
do this:
```bash ```bash
node node
...@@ -104,11 +153,18 @@ node ...@@ -104,11 +153,18 @@ node
> .exit > .exit
``` ```
#### Install mysql server ## Install mysql server
If you've followed the instructions up until this point you will already have a running condenser installation which is entirely acceptable for development purposes. It is *not required to run a SQL server for development*. If you're running a full-fledged site however, you will want to set one up. If you've followed the instructions up until this point you will already
have a running condenser installation which is entirely acceptable for
development purposes. It is *not required to run a SQL server for
development*. If you're running a full-fledged site however, you will want
to set one up.
Once set up, you can set the mysql server configuration option for condenser using the environment variable `SDC_DATABASE_URL`, or alternatively by editing it in `config/production.json`. You will use the format `mysql://user:pass@hostname/databasename`. Once set up, you can set the mysql server configuration option for
condenser using the environment variable `SDC_DATABASE_URL`, or
alternatively by editing it in `config/production.json`. You will use the
format `mysql://user:pass@hostname/databasename`.
Example: Example:
...@@ -116,7 +172,8 @@ Example: ...@@ -116,7 +172,8 @@ Example:
export SDC_DATABASE_URL="mysql://root:password@127.0.0.1/steemit_dev" export SDC_DATABASE_URL="mysql://root:password@127.0.0.1/steemit_dev"
``` ```
Here are instructions for setting up a mysql server and running the necessary migrations by operating system: Here are instructions for setting up a mysql server and running the
necessary migrations by operating system:
OS X: OS X:
...@@ -135,8 +192,8 @@ sudo apt-get update ...@@ -135,8 +192,8 @@ sudo apt-get update
sudo apt-get install mysql-server sudo apt-get install mysql-server
``` ```
On Ubuntu 16.04+ you may be unable to connect to mysql without root access, if On Ubuntu 16.04+ you may be unable to connect to mysql without root access,
so update the mysql root user as follows: if so update the mysql root user as follows:
``` ```
sudo mysql -u root sudo mysql -u root
...@@ -153,12 +210,14 @@ mysql -u root ...@@ -153,12 +210,14 @@ mysql -u root
> quit > quit
``` ```
#### Database migrations ### Database migrations
This is a required step in order for the database to be 'ready' for condenser's use. This is a required step in order for the database to be 'ready' for
condenser's use.
Edit the file `src/db/config/config.json` using your favorite command line
Edit the file `src/db/config/config.json` using your favorite command line text editor being sure that the username, password, host, and database name are set correctly and match your newly configured mysql setup. text editor being sure that the username, password, host, and database name
are set correctly and match your newly configured mysql setup.
Run `sequelize db:migrate` in `src/db` directory, like this: Run `sequelize db:migrate` in `src/db` directory, like this:
...@@ -167,22 +226,28 @@ cd src/db ...@@ -167,22 +226,28 @@ cd src/db
yarn exec sequelize db:migrate yarn exec sequelize db:migrate
``` ```
#### Style Guides For Submitting Pull Requests ## Style Guides For Submitting Pull Requests
##### File naming and location ### File naming and location
- Prefer CamelCase js and jsx file names - Prefer CamelCase js and jsx file names
- Prefer lower case one word directory names - Prefer lower case one word directory names
- Keep stylesheet files close to components - Keep stylesheet files close to components
- Component's stylesheet file name should match component name - Component's stylesheet file name should match component name
##### Js & Jsx #### Js & Jsx
We use [prettier](https://github.com/prettier/prettier) to autofromat the
code, with [this configuration](.prettierrc). Run `yarn run fmt` to format
everything in `src/`, or `yarn exec -- prettier --config .prettierrc
--write src/whatever/file.js` for a specific file.
We use [prettier](https://github.com/prettier/prettier) to autofromat the code, with [this configuration](.prettierrc). Run `yarn run fmt` to format everything in `src/`, or `yarn exec -- prettier --config .prettierrc --write src/whatever/file.js` for a specific file. #### CSS & SCSS
##### CSS & SCSS If a component requires a css rule, please use its uppercase name for the
If a component requires a css rule, please use its uppercase name for the class, e.g. "Header" class for the header's root div. class, e.g. "Header" class for the header's root div. We adhere to BEM
We adhere to BEM methodology with exception for Foundation classes, here is an example for the Header component: methodology with exception for Foundation classes, here is an example for
the Header component:
```html ```html
<!-- Block --> <!-- Block -->
...@@ -195,6 +260,10 @@ We adhere to BEM methodology with exception for Foundation classes, here is an e ...@@ -195,6 +260,10 @@ We adhere to BEM methodology with exception for Foundation classes, here is an e
</ul> </ul>
``` ```
## Storybook
`yarn run storybook`
## Testing ## Testing
### Run test suite ### Run test suite
......
...@@ -18,6 +18,7 @@ import ImageUserBlockList from 'app/utils/ImageUserBlockList'; ...@@ -18,6 +18,7 @@ import ImageUserBlockList from 'app/utils/ImageUserBlockList';
import proxifyImageUrl from 'app/utils/ProxifyUrl'; import proxifyImageUrl from 'app/utils/ProxifyUrl';
import { pathTo, convertPostPath } from 'app/Routes'; import { pathTo, convertPostPath } from 'app/Routes';
import Userpic, { avatarSize } from 'app/components/elements/Userpic'; import Userpic, { avatarSize } from 'app/components/elements/Userpic';
import { SIGNUP_URL } from 'shared/constants';
class PostSummary extends React.Component { class PostSummary extends React.Component {
static propTypes = { static propTypes = {
...@@ -273,11 +274,11 @@ class PostSummary extends React.Component { ...@@ -273,11 +274,11 @@ class PostSummary extends React.Component {
</span> </span>
) : ( ) : (
<span> <span>
<Link to={pathTo.signup()}> <a href={SIGNUP_URL}>
{tt( {tt(
'postsummary_jsx.create_an_account' 'postsummary_jsx.create_an_account'
)} )}
</Link>{' '} </a>{' '}
{tt( {tt(
'postsummary_jsx.to_save_your_preferences' 'postsummary_jsx.to_save_your_preferences'
)}. )}.
......
import React from 'react'; import React from 'react';
import { pathTo } from 'app/Routes'; import { pathTo } from 'app/Routes';
import { SIGNUP_URL } from 'shared/constants';
const SidebarNewUsers = () => ( const SidebarNewUsers = () => (
<div className="c-sidebar__module"> <div className="c-sidebar__module">
...@@ -24,7 +25,7 @@ const SidebarNewUsers = () => ( ...@@ -24,7 +25,7 @@ const SidebarNewUsers = () => (
</a> </a>
</li> </li>
<li className="c-sidebar__list-item"> <li className="c-sidebar__list-item">
<a className="c-sidebar__link" href={pathTo.signup()}> <a className="c-sidebar__link" href={SIGNUP_URL}>
Sign up Sign up
</a> </a>
</li> </li>
......
...@@ -5,6 +5,7 @@ import tt from 'counterpart'; ...@@ -5,6 +5,7 @@ import tt from 'counterpart';
import CloseButton from 'react-foundation-components/lib/global/close-button'; import CloseButton from 'react-foundation-components/lib/global/close-button';
import * as transactionActions from 'app/redux/TransactionReducer'; import * as transactionActions from 'app/redux/TransactionReducer';
import Icon from 'app/components/elements/Icon'; import Icon from 'app/components/elements/Icon';
import { DEBT_TOKEN_SHORT, INVEST_TOKEN_SHORT } from 'app/client_config';
import FormattedAsset from 'app/components/elements/FormattedAsset'; import FormattedAsset from 'app/components/elements/FormattedAsset';
import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate';
import { import {
...@@ -55,6 +56,7 @@ class Voting extends React.Component { ...@@ -55,6 +56,7 @@ class Voting extends React.Component {
post_obj: React.PropTypes.object, post_obj: React.PropTypes.object,
net_vesting_shares: React.PropTypes.number, net_vesting_shares: React.PropTypes.number,
voting: React.PropTypes.bool, voting: React.PropTypes.bool,
price_per_steem: React.PropTypes.number,
}; };
static defaultProps = { static defaultProps = {
...@@ -173,6 +175,7 @@ class Voting extends React.Component { ...@@ -173,6 +175,7 @@ class Voting extends React.Component {
net_vesting_shares, net_vesting_shares,
is_comment, is_comment,
post_obj, post_obj,
price_per_steem,
} = this.props; } = this.props;
const { username } = this.props; const { username } = this.props;
const { votingUp, votingDown, showWeight, weight, myVote } = this.state; const { votingUp, votingDown, showWeight, weight, myVote } = this.state;
...@@ -271,10 +274,18 @@ class Voting extends React.Component { ...@@ -271,10 +274,18 @@ class Voting extends React.Component {
const pending_payout = parsePayoutAmount( const pending_payout = parsePayoutAmount(
post_obj.get('pending_payout_value') post_obj.get('pending_payout_value')
); );
const percent_steem_dollars =
post_obj.get('percent_steem_dollars') / 20000;
const pending_payout_sbd = pending_payout * percent_steem_dollars;
const pending_payout_sp =
(pending_payout - pending_payout_sbd) / price_per_steem;
const promoted = parsePayoutAmount(post_obj.get('promoted')); const promoted = parsePayoutAmount(post_obj.get('promoted'));
const total_author_payout = parsePayoutAmount( const total_author_payout = parsePayoutAmount(
post_obj.get('total_payout_value') post_obj.get('total_payout_value')
); );
const author_payout_sbd = total_author_payout * percent_steem_dollars;
const author_payout_sp =
(total_author_payout - author_payout_sbd) / price_per_steem;
const total_curator_payout = parsePayoutAmount( const total_curator_payout = parsePayoutAmount(
post_obj.get('curator_payout_value') post_obj.get('curator_payout_value')
); );
...@@ -307,8 +318,20 @@ class Voting extends React.Component { ...@@ -307,8 +318,20 @@ class Voting extends React.Component {
value: formatDecimal(pending_payout).join(''), value: formatDecimal(pending_payout).join(''),
}), }),
}); });
} if (max_payout > 0) {
if (cashout_active) { payoutItems.push({
value:
'(' +
formatDecimal(pending_payout_sbd).join('') +
' ' +
DEBT_TOKEN_SHORT +
', ' +
formatDecimal(pending_payout_sp).join('') +
' ' +
INVEST_TOKEN_SHORT +
')',
});
}
payoutItems.push({ value: <TimeAgoWrapper date={cashout_time} /> }); payoutItems.push({ value: <TimeAgoWrapper date={cashout_time} /> });
} }
...@@ -341,6 +364,18 @@ class Voting extends React.Component { ...@@ -341,6 +364,18 @@ class Voting extends React.Component {
value: formatDecimal(total_author_payout).join(''), value: formatDecimal(total_author_payout).join(''),
}), }),
}); });
payoutItems.push({
value:
'(' +
formatDecimal(author_payout_sbd).join('') +
' ' +
DEBT_TOKEN_SHORT +
', ' +
formatDecimal(author_payout_sp).join('') +
' ' +
INVEST_TOKEN_SHORT +
')',
});
payoutItems.push({ payoutItems.push({
value: tt('voting_jsx.past_payouts_curators', { value: tt('voting_jsx.past_payouts_curators', {
value: formatDecimal(total_curator_payout).join(''), value: formatDecimal(total_curator_payout).join(''),
...@@ -502,6 +537,13 @@ export default connect( ...@@ -502,6 +537,13 @@ export default connect(
const voting = state.global.get( const voting = state.global.get(
`transaction_vote_active_${author}_${permlink}` `transaction_vote_active_${author}_${permlink}`
); );
let price_per_steem = undefined;
const feed_price = state.global.get('feed_price');
if (feed_price && feed_price.has('base') && feed_price.has('quote')) {
const { base, quote } = feed_price.toJS();
if (/ SBD$/.test(base) && / STEEM$/.test(quote))
price_per_steem = parseFloat(base.split(' ')[0]);
}
return { return {
post: ownProps.post, post: ownProps.post,
...@@ -516,6 +558,7 @@ export default connect( ...@@ -516,6 +558,7 @@ export default connect(
post_obj: post, post_obj: post,
loggedin: username != null, loggedin: username != null,
voting, voting,
price_per_steem,
}; };
}, },
......
...@@ -2,6 +2,7 @@ import React from 'react'; ...@@ -2,6 +2,7 @@ import React from 'react';
import CloseButton from 'react-foundation-components/lib/global/close-button'; import CloseButton from 'react-foundation-components/lib/global/close-button';
import { Link } from 'react-router'; import { Link } from 'react-router';
import tt from 'counterpart'; import tt from 'counterpart';
import { SIGNUP_URL } from 'shared/constants';
export default class WelcomePanel extends React.Component { export default class WelcomePanel extends React.Component {
constructor(props) { constructor(props) {
...@@ -32,13 +33,13 @@ export default class WelcomePanel extends React.Component { ...@@ -32,13 +33,13 @@ export default class WelcomePanel extends React.Component {
{tt('navigation.intro_paragraph')} {tt('navigation.intro_paragraph')}
</h4> </h4>
<div className="row buttonWrapper"> <div className="row buttonWrapper">
<Link <a
className="button button--primary fade-in--5" className="button button--primary fade-in--5"
to="/pick_account" href={SIGNUP_URL}
> >
{' '} {' '}
<b>{tt('navigation.sign_up')}</b>{' '} <b>{tt('navigation.sign_up')}</b>{' '}
</Link> </a>
<Link <Link
href="/faq.html" href="/faq.html"
......
...@@ -11,7 +11,7 @@ import { serverApiRecordEvent } from 'app/utils/ServerApiClient'; ...@@ -11,7 +11,7 @@ import { serverApiRecordEvent } from 'app/utils/ServerApiClient';
import tt from 'counterpart'; import tt from 'counterpart';
import { APP_URL } from 'app/client_config'; import { APP_URL } from 'app/client_config';
import { PrivateKey, PublicKey } from '@steemit/steem-js/lib/auth/ecc'; import { PrivateKey, PublicKey } from '@steemit/steem-js/lib/auth/ecc';
import { pathTo } from 'app/Routes'; import { SIGNUP_URL } from 'shared/constants';
class LoginForm extends Component { class LoginForm extends Component {
static propTypes = { static propTypes = {
...@@ -92,7 +92,7 @@ class LoginForm extends Component { ...@@ -92,7 +92,7 @@ class LoginForm extends Component {
const onType = document.getElementsByClassName('OpAction')[0] const onType = document.getElementsByClassName('OpAction')[0]
.textContent; .textContent;
serverApiRecordEvent('FreeMoneySignUp', onType); serverApiRecordEvent('FreeMoneySignUp', onType);
window.location.href = pathTo.signup(); window.location.href = SIGNUP_URL;
} }
SignIn() { SignIn() {
......
...@@ -12,6 +12,7 @@ import VerticalMenu from 'app/components/elements/VerticalMenu'; ...@@ -12,6 +12,7 @@ import VerticalMenu from 'app/components/elements/VerticalMenu';
import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import LoadingIndicator from 'app/components/elements/LoadingIndicator';
import NotifiCounter from 'app/components/elements/NotifiCounter'; import NotifiCounter from 'app/components/elements/NotifiCounter';
import { pathTo } from 'app/Routes'; import { pathTo } from 'app/Routes';
import { SIGNUP_URL } from 'shared/constants';
const defaultNavigate = e => { const defaultNavigate = e => {
if (e.metaKey || e.ctrlKey) { if (e.metaKey || e.ctrlKey) {
...@@ -152,13 +153,6 @@ function TopRightMenu({ ...@@ -152,13 +153,6 @@ function TopRightMenu({
if (probablyLoggedIn) { if (probablyLoggedIn) {
return ( return (
<ul className={mcn + mcl}> <ul className={mcn + mcl}>
{!vertical && (
<li className="Header__search">
<a href="/static/search.html" title={tt_search}>
<Icon name="search" />
</a>
</li>
)}
<li className={lcn} style={{ paddingTop: 0, paddingBottom: 0 }}> <li className={lcn} style={{ paddingTop: 0, paddingBottom: 0 }}>
<LoadingIndicator type="circle" inline /> <LoadingIndicator type="circle" inline />
</li> </li>
...@@ -182,7 +176,7 @@ function TopRightMenu({ ...@@ -182,7 +176,7 @@ function TopRightMenu({
</li> </li>
)} )}
<li className={lcn}> <li className={lcn}>
<a href={pathTo.signup()}>{tt('g.sign_up')}</a> <a href={SIGNUP_URL}>{tt('g.sign_up')}</a>
</li> </li>
<li className={lcn}> <li className={lcn}>
<a href={pathTo.login()} onClick={showLogin}> <a href={pathTo.login()} onClick={showLogin}>
......
...@@ -13,6 +13,7 @@ import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; ...@@ -13,6 +13,7 @@ import shouldComponentUpdate from 'app/utils/shouldComponentUpdate';
import { serverApiRecordEvent } from 'app/utils/ServerApiClient'; import { serverApiRecordEvent } from 'app/utils/ServerApiClient';
import { INVEST_TOKEN_UPPERCASE } from 'app/client_config'; import { INVEST_TOKEN_UPPERCASE } from 'app/client_config';
import { pathTo } from 'app/Routes'; import { pathTo } from 'app/Routes';
import { SIGNUP_URL } from 'shared/constants';
import { isLoggedIn } from 'app/utils/UserUtil'; import { isLoggedIn } from 'app/utils/UserUtil';
...@@ -31,7 +32,7 @@ class Post extends React.Component { ...@@ -31,7 +32,7 @@ class Post extends React.Component {
}; };
this.showSignUp = () => { this.showSignUp = () => {
serverApiRecordEvent('SignUp', 'Post Promo'); serverApiRecordEvent('SignUp', 'Post Promo');
window.location = pathTo.signup(); window.location = SIGNUP_URL;
}; };
this.shouldComponentUpdate = shouldComponentUpdate(this, 'Post'); this.shouldComponentUpdate = shouldComponentUpdate(this, 'Post');
} }
......
...@@ -57,20 +57,23 @@ export function validate_memo_field(value, username, memokey) { ...@@ -57,20 +57,23 @@ export function validate_memo_field(value, username, memokey) {
let suffix; let suffix;
value = value.split(' ').filter(v => v != ''); value = value.split(' ').filter(v => v != '');
for (var w in value) { for (var w in value) {
if (PrivateKey.isWif(value[w])) { // Only perform key tests if it might be a key, i.e. it is a long string.
return (suffix = 'Do not use private keys in memos. '); if (value[w].length >= 39) {
} if (/5[HJK]\w{40,45}/i.test(value[w])) {
if ( return (suffix =
memokey === 'Please do not include what appears to be a private key or password. ');
PrivateKey.fromSeed(username + 'memo' + value[w]) }
.toPublicKey() if (PrivateKey.isWif(value[w])) {
.toString() return (suffix = 'Do not use private keys in memos. ');
) { }
return (suffix = 'Do not use passwords in memos. '); if (
} memokey ===
if (/5[HJK]\w{40,45}/i.test(value[w])) { PrivateKey.fromSeed(username + 'memo' + value[w])
return (suffix = .toPublicKey()
'Please do not include what appears to be a private key or password. '); .toString()
) {
return (suffix = 'Do not use passwords in memos. ');
}
} }
} }
return null; return null;
......
...@@ -44,6 +44,9 @@ export default ` ...@@ -44,6 +44,9 @@ export default `
/elvinanurhaliza/photohraphy-dewy-flowers /elvinanurhaliza/photohraphy-dewy-flowers
/elvinanurhaliza/spider-finepix-s4800-reptiles-etc /elvinanurhaliza/spider-finepix-s4800-reptiles-etc
/seanfrederic/feature-film-loco-i-m-a-producer-on-starts-filming-soon /seanfrederic/feature-film-loco-i-m-a-producer-on-starts-filming-soon
/kriptonoob/palm-beach-confidential-report-cindicator-125
/medicbtom/palm-beach-confidential-released-their-monthly-pick
/vaerospace/fire-and-fury-pdf-direct-download-from-ipfs-a-great-giggle
` `
.trim() .trim()
.split('\n'); .split('\n');
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
@arg {object} initialValues required for checkboxes {save: false, ...} @arg {object} initialValues required for checkboxes {save: false, ...}
@arg {function} validation - values => ({ username: ! values.username ? 'Required' : null, ... }) @arg {function} validation - values => ({ username: ! values.username ? 'Required' : null, ... })
*/ */
const USER_INPUT_DEBOUNCE_MS = 400;
export default function reactForm({ export default function reactForm({
name, name,
instance, instance,
...@@ -14,7 +12,6 @@ export default function reactForm({ ...@@ -14,7 +12,6 @@ export default function reactForm({
initialValues, initialValues,
validation = () => {}, validation = () => {},
}) { }) {
let debounce = { timeout: null };
if (typeof instance !== 'object') if (typeof instance !== 'object')
throw new TypeError('instance is a required object'); throw new TypeError('instance is a required object');
if (!Array.isArray(fields)) if (!Array.isArray(fields))
...@@ -124,13 +121,7 @@ export default function reactForm({ ...@@ -124,13 +121,7 @@ export default function reactForm({
} }
instance.setState({ [fieldName]: v }, () => { instance.setState({ [fieldName]: v }, () => {
setFormStateDebounce( setFormState(name, instance, fields, validation);
name,
instance,
fields,
validation,
debounce
);
}); });
}; };
...@@ -143,13 +134,6 @@ export default function reactForm({ ...@@ -143,13 +134,6 @@ export default function reactForm({
} }
} }
function setFormStateDebounce(name, instance, fields, validation, debounce) {
clearTimeout(debounce.timeout);
debounce.timeout = setTimeout(function() {
setFormState(name, instance, fields, validation);
}, USER_INPUT_DEBOUNCE_MS);
}
function setFormState(name, instance, fields, validation) { function setFormState(name, instance, fields, validation) {
let formValid = true; let formValid = true;
let formTouched = false; let formTouched = false;
......
...@@ -304,18 +304,17 @@ export default function useGeneralApi(app) { ...@@ -304,18 +304,17 @@ export default function useGeneralApi(app) {
* secret * secret
*/ */
router.post('/create_user', koaBody, function*() { router.post('/create_user', koaBody, function*() {
if (rateLimitReq(this, this.req)) return;
const { name, email, owner_key, secret } = const { name, email, owner_key, secret } =
typeof this.request.body === 'string' typeof this.request.body === 'string'
? JSON.parse(this.request.body) ? JSON.parse(this.request.body)
: this.request.body; : this.request.body;
if (secret !== process.env.CREATE_USER_SECRET)
throw new Error('invalid secret');
logRequest('create_user', this, { name, email, owner_key }); logRequest('create_user', this, { name, email, owner_key });
try { try {
if (secret !== process.env.CREATE_USER_SECRET)
throw new Error('invalid secret');
if (!emailRegex.test(email.toLowerCase())) if (!emailRegex.test(email.toLowerCase()))
throw new Error('not valid email: ' + email); throw new Error('not valid email: ' + email);
const existingUser = yield findUser({ const existingUser = yield findUser({
......
...@@ -2,6 +2,7 @@ import koa_router from 'koa-router'; ...@@ -2,6 +2,7 @@ import koa_router from 'koa-router';
const redirects = [ const redirects = [
// example: [/\/about(\d+)-(.+)/, '/about?$0:$1', 302], // example: [/\/about(\d+)-(.+)/, '/about?$0:$1', 302],
[/^\с\/pick_account.*/, 'https://signup.steemit.com'],
]; ];
export default function useRedirects(app) { export default function useRedirects(app) {
......
export const PARAM_VIEW_MODE = 'view_mode'; export const PARAM_VIEW_MODE = 'view_mode';
export const VIEW_MODE_WHISTLE = 'whistle'; export const VIEW_MODE_WHISTLE = 'whistle';
export const WHISTLE_SIGNUP_COMPLETE = 'whistle_signup_complete'; export const WHISTLE_SIGNUP_COMPLETE = 'whistle_signup_complete';
export const SIGNUP_URL = 'https://signup.steemit.com';
...@@ -7862,7 +7862,7 @@ prop-types-extra@^1.0.1: ...@@ -7862,7 +7862,7 @@ prop-types-extra@^1.0.1:
dependencies: dependencies:
warning "^3.0.0" warning "^3.0.0"
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0: prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0:
version "15.6.0" version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies: dependencies:
...@@ -9354,6 +9354,12 @@ store@1.3.20: ...@@ -9354,6 +9354,12 @@ store@1.3.20:
version "1.3.20" version "1.3.20"
resolved "https://registry.yarnpkg.com/store/-/store-1.3.20.tgz#13ea7e3fb2d6c239868265d686b1d84e99c5be3e" resolved "https://registry.yarnpkg.com/store/-/store-1.3.20.tgz#13ea7e3fb2d6c239868265d686b1d84e99c5be3e"
storybook-addon-intl@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/storybook-addon-intl/-/storybook-addon-intl-2.3.0.tgz#94711276cefeb0b9ef13b1e70a96516c1fe0cfd2"
dependencies:
prop-types "^15.5.0"
stream-browserify@^2.0.1: stream-browserify@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment