From 4751951bc5027c32234c0e6a2ca4e4dc79b25241 Mon Sep 17 00:00:00 2001 From: gl2748 Date: Tue, 8 Aug 2017 08:52:39 -0400 Subject: [PATCH 001/399] fix, display total votes in post summary, refs #1596 --- src/app/components/cards/PostSummary.jsx | 2 +- src/app/components/elements/VotesAndComments.jsx | 5 ----- src/app/components/elements/VotesAndComments.scss | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/app/components/cards/PostSummary.jsx b/src/app/components/cards/PostSummary.jsx index ac6aa8d17..c80bfd945 100644 --- a/src/app/components/cards/PostSummary.jsx +++ b/src/app/components/cards/PostSummary.jsx @@ -124,7 +124,7 @@ class PostSummary extends React.Component { ); const content_footer = (
- + {!archived && } diff --git a/src/app/components/elements/VotesAndComments.jsx b/src/app/components/elements/VotesAndComments.jsx index 415566601..bbae63ef6 100644 --- a/src/app/components/elements/VotesAndComments.jsx +++ b/src/app/components/elements/VotesAndComments.jsx @@ -13,7 +13,6 @@ class VotesAndComments extends React.Component { commentsLink: React.PropTypes.string.isRequired, // Redux connect properties - votes: React.PropTypes.number, comments: React.PropTypes.number, }; @@ -29,9 +28,6 @@ class VotesAndComments extends React.Component { return ( - -  {votes} - 1 ? 'chatboxes' : 'chatbox'} /> {comments} @@ -48,7 +44,6 @@ export default connect( if (!post) return props; return { ...props, - votes: post.get('net_votes'), comments: post.get('children') }; } diff --git a/src/app/components/elements/VotesAndComments.scss b/src/app/components/elements/VotesAndComments.scss index fc00adca4..234cdc6f4 100644 --- a/src/app/components/elements/VotesAndComments.scss +++ b/src/app/components/elements/VotesAndComments.scss @@ -13,11 +13,11 @@ .VotesAndComments__votes { padding-right: 1rem; - border-right: 1px solid $medium-gray; } .VotesAndComments__comments { padding-left: 1rem; + border-left: 1px solid $medium-gray; } /* Small only */ -- GitLab From a43ca8b4c2c94bb9c6f604626738ac2c9fad6381 Mon Sep 17 00:00:00 2001 From: gl2748 Date: Tue, 15 Aug 2017 07:37:58 -0400 Subject: [PATCH 002/399] fix, display total votes in post summary,(#1596) --- src/app/components/cards/PostSummary.jsx | 2 +- src/app/components/elements/VotesAndComments.jsx | 8 +++++++- src/app/components/elements/VotesAndComments.scss | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/app/components/cards/PostSummary.jsx b/src/app/components/cards/PostSummary.jsx index c80bfd945..ac6aa8d17 100644 --- a/src/app/components/cards/PostSummary.jsx +++ b/src/app/components/cards/PostSummary.jsx @@ -124,7 +124,7 @@ class PostSummary extends React.Component { ); const content_footer = (
- + {!archived && } diff --git a/src/app/components/elements/VotesAndComments.jsx b/src/app/components/elements/VotesAndComments.jsx index bbae63ef6..801faf1c9 100644 --- a/src/app/components/elements/VotesAndComments.jsx +++ b/src/app/components/elements/VotesAndComments.jsx @@ -14,6 +14,7 @@ class VotesAndComments extends React.Component { // Redux connect properties comments: React.PropTypes.number, + post_obj: React.PropTypes.object, }; constructor(props) { @@ -22,12 +23,16 @@ class VotesAndComments extends React.Component { } render() { - const {votes, comments, commentsLink} = this.props; + const {comments, commentsLink, post_obj} = this.props; + const total_votes = post_obj.getIn(['stats', 'total_votes']); let comments_tooltip = tt('votesandcomments_jsx.no_responses_yet_click_to_respond'); if (comments > 0) comments_tooltip = `${tt('votesandcomments_jsx.response_count', {count: comments})}. ${tt('votesandcomments_jsx.click_to_respond')}.` return ( + +  {total_votes} + 1 ? 'chatboxes' : 'chatbox'} /> {comments} @@ -44,6 +49,7 @@ export default connect( if (!post) return props; return { ...props, + post_obj: post, comments: post.get('children') }; } diff --git a/src/app/components/elements/VotesAndComments.scss b/src/app/components/elements/VotesAndComments.scss index 234cdc6f4..fc00adca4 100644 --- a/src/app/components/elements/VotesAndComments.scss +++ b/src/app/components/elements/VotesAndComments.scss @@ -13,11 +13,11 @@ .VotesAndComments__votes { padding-right: 1rem; + border-right: 1px solid $medium-gray; } .VotesAndComments__comments { padding-left: 1rem; - border-left: 1px solid $medium-gray; } /* Small only */ -- GitLab From 39660542a6c44e55b6f53909958a7088d54f86d0 Mon Sep 17 00:00:00 2001 From: Nate Brune Date: Fri, 1 Sep 2017 14:11:17 -0400 Subject: [PATCH 003/399] #1535 linted more app components --- src/app/components/cards/PostHistoryRow.jsx | 41 ++++--- src/app/components/cards/PostSummary.jsx | 111 +++++++++--------- src/app/components/cards/PostsList.jsx | 57 +++++---- .../components/cards/TransferHistoryRow.jsx | 29 +++-- src/app/components/cards/UserListRow.jsx | 17 +-- src/app/components/cards/VoteHistoryRow.jsx | 41 ++++--- 6 files changed, 148 insertions(+), 148 deletions(-) diff --git a/src/app/components/cards/PostHistoryRow.jsx b/src/app/components/cards/PostHistoryRow.jsx index daec0888a..7499a35e5 100644 --- a/src/app/components/cards/PostHistoryRow.jsx +++ b/src/app/components/cards/PostHistoryRow.jsx @@ -5,19 +5,18 @@ import Icon from 'app/components/elements/Icon'; import tt from 'counterpart'; export default class PostHistoryRow extends React.Component { - render() { - let op = this.props.op; + const op = this.props.op; console.log( "op: ", op ); - let context = this.props.context; /// account perspective + const context = this.props.context; /// account perspective - let parent_author = op[1].op[1].parent_author; - let author = op[1].op[1].author; - let parent_link = "/@" + parent_author; - let author_link = "/@" + author; - let parent_perm = op[1].op[1].parent_permlink; - let permlink = op[1].op[1].permlink; - let in_reply_to = ; + const parent_author = op[1].op[1].parent_author; + const author = op[1].op[1].author; + const parent_link = "/@" + parent_author; + const author_link = "/@" + author; + const parent_perm = op[1].op[1].parent_permlink; + const permlink = op[1].op[1].permlink; + let in_reply_to = ; if (parent_author && parent_author != context) in_reply_to = {tt('g.in_reply_to') + ' '}@{parent_author}; else if (parent_author == context) @@ -25,21 +24,21 @@ export default class PostHistoryRow extends React.Component { // const content_markdown = op[1].op[1].body; // const body = () - let post_link = '/' + op[1].op[1].parent_permlink + author_link + '/' + permlink; + const post_link = '/' + op[1].op[1].parent_permlink + author_link + '/' + permlink; return(
-
-
-

{op[1].op[1].title}

-
+
+
+

{op[1].op[1].title}

-
-
- - {in_reply_to} -
+
+
+
+ + {in_reply_to}
+
); } -}; +} diff --git a/src/app/components/cards/PostSummary.jsx b/src/app/components/cards/PostSummary.jsx index 88f321288..a179711ab 100644 --- a/src/app/components/cards/PostSummary.jsx +++ b/src/app/components/cards/PostSummary.jsx @@ -75,15 +75,15 @@ class PostSummary extends React.Component { if(reblogged_by) { reblogged_by = (
- {tt('postsummary_jsx.resteemed_by')} -
) + {tt('postsummary_jsx.resteemed_by')} +
) } // 'account' is the current blog being viewed, if applicable. if(account && account != content.get('author')) { reblogged_by = (
- {tt('postsummary_jsx.resteemed')} -
) + {tt('postsummary_jsx.resteemed')} +
) } const {gray, authorRepLog10, flagWeight, isNsfw} = content.get('stats', Map()).toJS() @@ -107,32 +107,32 @@ class PostSummary extends React.Component { } const content_body = (); const content_title = (

- navigate(e, onClick, post, title_link_url)}> - {isNsfw && nsfw} - {title_text} - {full_power && } - + navigate(e, onClick, post, title_link_url)}> + {isNsfw && nsfw} + {title_text} + {full_power && } +

); // author and category const author_category = ( - navigate(e, onClick, post, title_link_url)}> - {} {tt('g.by')} - {} {tt('g.in')} + navigate(e, onClick, post, title_link_url)}> + {} {tt('g.by')} + {} {tt('g.in')} ); const content_footer = (
- - - - {!archived && } - - {author_category} - + + + + {!archived && } + + {author_category} +
) const {nsfwPref, username} = this.props @@ -145,18 +145,18 @@ class PostSummary extends React.Component { } else if(nsfwPref === 'warn' && !revealNsfw) { // user wishes to be warned, and has not revealed this post return ( -
-
-
- {author_category} -
- {tt('postsummary_jsx.this_post_is')} nsfw. +
+
+
+ {author_category} +
+ {tt('postsummary_jsx.this_post_is')} nsfw. {tt('postsummary_jsx.you_can')} {tt('postsummary_jsx.reveal_it')} {tt('g.or') + ' '} - {username ? {tt('postsummary_jsx.adjust_your')} {tt('postsummary_jsx.display_preferences')}. + {username ? {tt('postsummary_jsx.adjust_your')} {tt('postsummary_jsx.display_preferences')}. : {tt('postsummary_jsx.create_an_account')} {tt('postsummary_jsx.to_save_your_preferences')}.} - {content_footer} -
-
+ {content_footer} +
+
) } } @@ -171,33 +171,33 @@ class PostSummary extends React.Component { if(thumbSize == 'mobile') { thumb = navigate(e, onClick, post, p.link)} className="PostSummary__image-mobile"> } else { - thumb = navigate(e, onClick, post, p.link)} className="PostSummary__image" style={{backgroundImage: 'url(' + url + ')'}}> + thumb = navigate(e, onClick, post, p.link)} className="PostSummary__image" style={{backgroundImage: 'url(' + url + ')'}} /> } } const commentClasses = [] if(gray || ignore) commentClasses.push('downvoted') // rephide return ( -
-
0 ? '' : 'PostSummary__collapse'}> -
-
- {reblogged_by} -
- {content_title} -
-
- {author_category} -
- {thumb} -
-
- {content_title} -
- {content_body} - {content_footer} -
-
+
+
0 ? '' : 'PostSummary__collapse'}> +
+
+ {reblogged_by} +
+ {content_title} +
+
+ {author_category} +
+ {thumb} +
+
+ {content_title} +
+ {content_body} + {content_footer} +
+
) } } @@ -213,13 +213,16 @@ export default connect( total_payout = content.get('total_payout_value'); } return { - post, content, pending_payout, total_payout, + post, +content, +pending_payout, +total_payout, username: state.user.getIn(['current', 'username']) || state.offchain.get('account') }; }, - (dispatch) => ({ - dispatchSubmit: data => { dispatch(user.actions.usernamePasswordLogin({...data})) }, + dispatch => ({ + dispatchSubmit: (data) => { dispatch(user.actions.usernamePasswordLogin({...data})) }, clearError: () => { dispatch(user.actions.loginError({error: null})) } }) )(PostSummary) diff --git a/src/app/components/cards/PostsList.jsx b/src/app/components/cards/PostsList.jsx index ff1140f4f..a35ccdf92 100644 --- a/src/app/components/cards/PostsList.jsx +++ b/src/app/components/cards/PostsList.jsx @@ -18,7 +18,6 @@ function topPosition(domElt) { } class PostsList extends React.Component { - static propTypes = { posts: PropTypes.object.isRequired, loading: PropTypes.bool.isRequired, @@ -187,37 +186,37 @@ class PostsList extends React.Component { if(!(ignore || hide) || showSpam) // rephide postsInfo.push({item, ignore}) }); - const renderSummary = items => items.map(item =>
  • - items.map(item => (
  • + -
  • ) + )) return ( -
    -
      - {renderSummary(postsInfo)} -
    - {loading &&
    } - {showPost &&
    - -
    - -
    -
    } -
    +
    +
      + {renderSummary(postsInfo)} +
    + {loading &&
    } + {showPost &&
    + +
    + +
    +
    } +
    ); } } diff --git a/src/app/components/cards/TransferHistoryRow.jsx b/src/app/components/cards/TransferHistoryRow.jsx index ea1db55ba..e3b72f63c 100644 --- a/src/app/components/cards/TransferHistoryRow.jsx +++ b/src/app/components/cards/TransferHistoryRow.jsx @@ -76,8 +76,7 @@ class TransferHistoryRow extends React.Component { // other_account = ``; description_end = ''; } else if (type === 'claim_reward_balance') { - - let rewards = []; + const rewards = []; if(parseFloat(data.reward_steem.split(' ')[0]) > 0) rewards.push(data.reward_steem); if(parseFloat(data.reward_sbd.split(' ')[0]) > 0) rewards.push(data.reward_sbd); if(parseFloat(data.reward_vests.split(' ')[0]) > 0) rewards.push(`${reward_vests} STEEM POWER`); @@ -119,19 +118,19 @@ class TransferHistoryRow extends React.Component { } // return( - - - - - - {description_start} - {other_account && {other_account}} - {description_end} - - - - - + + + + + + {description_start} + {other_account && {other_account}} + {description_end} + + + + + ); } } diff --git a/src/app/components/cards/UserListRow.jsx b/src/app/components/cards/UserListRow.jsx index afd0dbc05..bb5e71e8e 100644 --- a/src/app/components/cards/UserListRow.jsx +++ b/src/app/components/cards/UserListRow.jsx @@ -6,19 +6,20 @@ class UserListRow extends React.Component { render() { const {user, loggedIn} = this.props return( - - {loggedIn && - - } - - {user} - - + + {loggedIn && + + } + + {user} + + ); } } import {connect} from 'react-redux' + export default connect( (state, ownProps) => { const loggedIn = state.user.hasIn(['current', 'username']) diff --git a/src/app/components/cards/VoteHistoryRow.jsx b/src/app/components/cards/VoteHistoryRow.jsx index b57b86872..c8d8fa580 100644 --- a/src/app/components/cards/VoteHistoryRow.jsx +++ b/src/app/components/cards/VoteHistoryRow.jsx @@ -5,19 +5,18 @@ import Icon from 'app/components/elements/Icon'; import tt from 'counterpart'; export default class VoteHistoryRow extends React.Component { - render() { - let op = this.props.op; + const op = this.props.op; // console.log( "op: ", op ); - let context = this.props.context; /// account perspective + const context = this.props.context; /// account perspective - let parent_author = op[1].op[1].parent_author; - let author = op[1].op[1].author; - let parent_link = "/@" + parent_author; - let author_link = "/@" + author; - let parent_perm = op[1].op[1].parent_permlink; - let permlink = op[1].op[1].permlink; - let in_reply_to = ; + const parent_author = op[1].op[1].parent_author; + const author = op[1].op[1].author; + const parent_link = "/@" + parent_author; + const author_link = "/@" + author; + const parent_perm = op[1].op[1].parent_permlink; + const permlink = op[1].op[1].permlink; + let in_reply_to = ; if( parent_author && parent_author != context ) in_reply_to = {tt('g.in_reply_to')} @{parent_author}; else if( parent_author == context ) @@ -25,22 +24,22 @@ export default class VoteHistoryRow extends React.Component { // const content_markdown = op[1].op[1].body; // const body = () - let post_link = '/' + op[1].op[1].parent_permlink + author_link + '/' + permlink; + const post_link = '/' + op[1].op[1].parent_permlink + author_link + '/' + permlink; return(
    -
    -
    -

    {op[1].op[1].title}

    -
    +
    +
    +

    {op[1].op[1].title}

    -
    -
    - - {in_reply_to} -
    +
    +
    +
    + + {in_reply_to}
    +
    ); } -}; +} -- GitLab From 96fd63b73a6d12c0e3f217e7cd77c3757baa1e85 Mon Sep 17 00:00:00 2001 From: Jeffrey Paul Date: Tue, 5 Sep 2017 14:35:04 -0400 Subject: [PATCH 004/399] Update Dockerfile --- Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4cc9e00cc..004867113 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,9 +19,7 @@ COPY . /var/app # ./node_modules/.bin/eslint . && \ # npm run build -RUN mkdir tmp && \ - npm test && \ - npm run-script build +RUN yarn run test && yarn run build ENV PORT 8080 ENV NODE_ENV production -- GitLab From c7790cb13ce64be9a71eddee1d8b8c16e91df3b3 Mon Sep 17 00:00:00 2001 From: plink01001 Date: Tue, 26 Sep 2017 19:38:19 -0400 Subject: [PATCH 005/399] Add spam and abuse question to FAQ --- src/app/help/en/faq.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index ae8765492..d817b20fe 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -126,7 +126,8 @@ ### Plagiarism, Spam, and Abuse -- What are Steemit’s policies on plagiarism and spam? +- What is considered spam or abuse? +- What are Steemit’s policies on plagiarism? - Is it okay to use random pictures from the internet? - What is Steemcleaners? - What is @cheetah? @@ -1023,8 +1024,25 @@ With the current implementation, there is no difference between a downvote and a ^ # Plagiarism, Spam, and Abuse - -## What are Steemit’s policies on plagiarism and spam? + +## What is considered spam or abuse? + +- Asking for money, views, upvotes, follows, or resteems. +- Leaving nearly identical or materially similar comments on multiple posts. +- Comments that are unrelated to the topic of discussion. +- Sending unsolicited links or requests to users via wallet memos. +- Posts that require upvotes to enter or play in a contest or game. +- Sending users a link to your blog or a post if it is not relevant to the conversation. +- Posts or comments that include little or nothing more than an offer to trade follows or upvotes. +- Using tags that are unrelated to the post. +- Threatening users with any type of physical violence. +- Not citing sources when using someone else’s material. +- Posting ‘not safe for work’ content without using the “nsfw” tag. +- Scams or Fraudulent offers. + +^ + +## What are Steemit’s policies on plagiarism? If you are posting plagiarized or copied content, you can get in legal trouble for violating copyright laws. Plagiarized posts and spam are seen as abuse and will be downvoted by community members. If you are posting or using someone else’s content, you must ensure that you have the rights to use the content, and properly reference the sources where you got the material from. -- GitLab From b0b80a185634b30f430b445e5179e4a214c54825 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Wed, 27 Sep 2017 21:35:23 -0500 Subject: [PATCH 006/399] add buy votes/resteems/follows --- src/app/help/en/faq.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index d817b20fe..c366affb1 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1038,6 +1038,7 @@ With the current implementation, there is no difference between a downvote and a - Threatening users with any type of physical violence. - Not citing sources when using someone else’s material. - Posting ‘not safe for work’ content without using the “nsfw” tag. +- Selling or offering to buy votes/resteems/follows, or schemes that facilitate this. - Scams or Fraudulent offers. ^ -- GitLab From ddb3853d5e2236d2e7dbf2f2a8899c1de9898a41 Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Wed, 11 Oct 2017 00:06:12 +0200 Subject: [PATCH 007/399] Move offchain state to separate api call --- src/app/Main.js | 53 +++++++++++++++++++++++++++------- src/server/api/general.js | 22 ++++++++++++++ src/shared/UniversalRender.jsx | 17 ++++++++++- 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/app/Main.js b/src/app/Main.js index 0d124d889..ebac3bc02 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -26,7 +26,30 @@ try { console.error(e) } -function runApp(initial_state) { +let offchain // loaded by main() + +// iso default selector that injects offchain state +const isoSelector = () => { + const all = document.querySelectorAll('[data-iso-key]') + return Array.prototype.reduce.call(all, (cache, node) => { + const key = node.getAttribute('data-iso-key') + if (!cache[key]) cache[key] = {} + if (node.nodeName === 'SCRIPT') { + try { + const state = JSON.parse(node.innerHTML) + state.offchain = offchain + cache[key].state = state + } catch (e) { + cache[key].state = {} + } + } else { + cache[key].node = node + } + return cache + }, {}) +} + +async function runApp(initial_state) { console.log('Initial state', initial_state); const konami = { code: 'xyzzy', @@ -96,14 +119,24 @@ function runApp(initial_state) { }); } -if (!window.Intl) { - require.ensure(['intl/dist/Intl'], (require) => { - window.IntlPolyfill = window.Intl = require('intl/dist/Intl') - require('intl/locale-data/jsonp/en-US.js') - require('intl/locale-data/jsonp/es.js') - Iso.bootstrap(runApp); - }, "IntlBundle"); +async function getOffchainState() { + return (await fetch('/api/v1/state')).json() } -else { - Iso.bootstrap(runApp); + +async function main() { + offchain = getOffchainState() + console.log("GOT THE OFF") + if (!window.Intl) { + require.ensure(['intl/dist/Intl'], (require) => { + window.IntlPolyfill = window.Intl = require('intl/dist/Intl') + require('intl/locale-data/jsonp/en-US.js') + require('intl/locale-data/jsonp/es.js') + Iso.bootstrap(runApp, isoSelector); + }, "IntlBundle"); + } + else { + Iso.bootstrap(runApp, isoSelector); + } } + +main() diff --git a/src/server/api/general.js b/src/server/api/general.js index dd766c530..b0aeb6bad 100644 --- a/src/server/api/general.js +++ b/src/server/api/general.js @@ -12,6 +12,7 @@ import Mixpanel from 'mixpanel'; import Tarantool from 'db/tarantool'; import {PublicKey, Signature, hash} from 'steem/lib/auth/ecc'; import {api, broadcast} from 'steem'; +import secureRandom from 'secure-random'; const mixpanel = config.get('mixpanel') ? Mixpanel.init(config.get('mixpanel')) : null; @@ -44,6 +45,27 @@ export default function useGeneralApi(app) { app.use(router.routes()); const koaBody = koa_body(); + router.get('/state', function *() { + const ctx = this; + let login_challenge = ctx.session.login_challenge; + if (!login_challenge) { + login_challenge = secureRandom.randomBuffer(16).toString('hex'); + ctx.session.login_challenge = login_challenge; + } + const offchain = { + csrf: ctx.csrf, + flash: ctx.flash, + new_visit: ctx.session.new_visit, + account: ctx.session.a, + config: $STM_Config, + uid: ctx.session.uid, + serverBusy: false, + signup_bonus: '$1', // TODO: don't hardcode this + login_challenge + }; + this.body = JSON.stringify(offchain); + }) + router.post('/accounts_wait', koaBody, function *() { if (rateLimitReq(this, this.req)) return; const params = this.request.body; diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index e70af7ba0..4b8c990ae 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -30,6 +30,21 @@ import {contentStats} from 'app/utils/StateFunctions' import {api} from 'steem'; +// iso renderer, same as defaultRenderer except we drop the offchain data +const isoRenderer = { + markup(html, key) { + if (!html) return '' + return `
    ${html}
    ` + }, + data(state, key) { + if (!state) return '' + const s = JSON.parse(state) + delete s.offchain + state = JSON.stringify(s) + return `` + }, +} + const sagaMiddleware = createSagaMiddleware( ...userWatches, // keep first to remove keys early when a page change happens ...fetchDataWatches, @@ -221,7 +236,7 @@ async function universalRender({ location, initial_state, offchain, ErrorPage, t titleBase: 'Steemit - ', meta, statusCode: status, - body: Iso.render(app, server_store.getState()) + body: Iso.render(app, server_store.getState(), '', isoRenderer) }; } -- GitLab From 5f56addf04ffe4497999ae9d2998cd51e305c76f Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Wed, 11 Oct 2017 00:10:37 +0200 Subject: [PATCH 008/399] Typofix --- src/app/Main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/Main.js b/src/app/Main.js index ebac3bc02..d3803f404 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -125,7 +125,6 @@ async function getOffchainState() { async function main() { offchain = getOffchainState() - console.log("GOT THE OFF") if (!window.Intl) { require.ensure(['intl/dist/Intl'], (require) => { window.IntlPolyfill = window.Intl = require('intl/dist/Intl') -- GitLab From 5721f7952e0387c0000e7c4cd188d1a8a9752a46 Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Wed, 11 Oct 2017 00:20:30 +0200 Subject: [PATCH 009/399] Typofix again --- src/app/Main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Main.js b/src/app/Main.js index d3803f404..cd53844b1 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -124,7 +124,7 @@ async function getOffchainState() { } async function main() { - offchain = getOffchainState() + offchain = await getOffchainState() if (!window.Intl) { require.ensure(['intl/dist/Intl'], (require) => { window.IntlPolyfill = window.Intl = require('intl/dist/Intl') -- GitLab From 0b0405d255dd2f24ec224bb5ba0cf0413158ad6f Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Wed, 11 Oct 2017 01:21:44 +0200 Subject: [PATCH 010/399] Only set cookies for state url --- src/server/server.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/server/server.js b/src/server/server.js index d48baa8a7..1eb4b583f 100644 --- a/src/server/server.js +++ b/src/server/server.js @@ -49,6 +49,14 @@ const numProcesses = process.env.NUM_PROCESSES || os.cpus().length; app.use(requestTime(numProcesses)); +// drop set-cookie headers for everything but state api +app.use(function*(next) { + yield* next; + if (this.request.url !== '/api/v1/state') { + delete this.response.remove('set-cookie'); + } +}); + app.keys = [config.get('session_key')]; const crypto_key = config.get('server_session_secret'); -- GitLab From 4d7aaf165ea96180b694ef778405a36526c9b0a1 Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Wed, 11 Oct 2017 12:23:51 +0200 Subject: [PATCH 011/399] Send cookie with state request --- src/app/Main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Main.js b/src/app/Main.js index cd53844b1..92d0af103 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -120,7 +120,7 @@ async function runApp(initial_state) { } async function getOffchainState() { - return (await fetch('/api/v1/state')).json() + return (await fetch('/api/v1/state', {credentials: 'same-origin'})).json() } async function main() { -- GitLab From 5ceb01e0741dd7233aac3693519d3a9a6f0e854b Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Thu, 12 Oct 2017 16:26:18 +0200 Subject: [PATCH 012/399] Move account recovery state to state api Also remove unnecessary user lookup --- src/server/api/general.js | 9 +++++++ src/server/app_render.jsx | 54 --------------------------------------- 2 files changed, 9 insertions(+), 54 deletions(-) diff --git a/src/server/api/general.js b/src/server/api/general.js index b0aeb6bad..4068fe8cf 100644 --- a/src/server/api/general.js +++ b/src/server/api/general.js @@ -63,6 +63,15 @@ export default function useGeneralApi(app) { signup_bonus: '$1', // TODO: don't hardcode this login_challenge }; + if (ctx.session.arec) { + const account_recovery_record = yield models.AccountRecoveryRequest.findOne({ + attributes: ['id', 'account_name', 'status', 'provider'], + where: {id: ctx.session.arec, status: 'confirmed'} + }); + if (account_recovery_record) { + offchain.recover_account = account_recovery_record.account_name; + } + } this.body = JSON.stringify(offchain); }) diff --git a/src/server/app_render.jsx b/src/server/app_render.jsx index 05a1171f5..36a9f94cc 100644 --- a/src/server/app_render.jsx +++ b/src/server/app_render.jsx @@ -29,60 +29,6 @@ async function appRender(ctx) { uid: ctx.session.uid, login_challenge }; - const user_id = ctx.session.user; - if (user_id) { - let user = null; - if (appRender.dbStatus.ok || (new Date() - appRender.dbStatus.lastAttempt) > DB_RECONNECT_TIMEOUT) { - try { - user = await models.User.findOne({ - attributes: ['name', 'email', 'picture_small', 'account_status'], - where: {id: user_id}, - include: [{model: models.Account, attributes: ['name', 'ignored', 'created', 'owner_key']}], - order: 'Accounts.id desc', - logging: false - }); - appRender.dbStatus = {ok: true}; - } catch (e) { - appRender.dbStatus = {ok: false, lastAttempt: new Date()}; - console.error('WARNING! mysql query failed: ', e.toString()); - offchain.serverBusy = true; - } - } else { - offchain.serverBusy = true; - } - if (user) { - let account = null; - let account_has_keys = null; - for (const a of user.Accounts) { - if (!a.ignored) { - account = a.name; - if (a.owner_key && !a.created) { - account_has_keys = true; - } - break; - } - } - offchain.user = { - id: user_id, - name: user.name, - email: user.email, - picture: user.picture_small, - prv: ctx.session.prv, - account_status: user.account_status, - account, - account_has_keys - } - } - } - if (ctx.session.arec) { - const account_recovery_record = await models.AccountRecoveryRequest.findOne({ - attributes: ['id', 'account_name', 'status', 'provider'], - where: {id: ctx.session.arec, status: 'confirmed'} - }); - if (account_recovery_record) { - offchain.recover_account = account_recovery_record.account_name; - } - } const { body, title, statusCode, meta } = await universalRender({location: ctx.request.url, store, offchain, ErrorPage, tarantool: Tarantool.instance()}); -- GitLab From 4ce905ffa5f43873f55a7788c7f84f1763441a92 Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Thu, 12 Oct 2017 16:27:29 +0200 Subject: [PATCH 013/399] Move new_visit state to browser --- src/app/Main.js | 8 +++++++- src/app/components/App.scss | 15 ++++++++++++++- src/server/api/general.js | 1 - src/server/app_render.jsx | 2 -- src/server/server.js | 2 -- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/app/Main.js b/src/app/Main.js index 92d0af103..e5faf45e9 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -120,11 +120,17 @@ async function runApp(initial_state) { } async function getOffchainState() { - return (await fetch('/api/v1/state', {credentials: 'same-origin'})).json() + const state = await (await fetch('/api/v1/state', {credentials: 'same-origin'})).json() + const now = Date.now() / 1000 + const lastVisit = localStorage.getItem('lastVisit') || -Infinity + const loginData = localStorage.getItem('autopost2') + state.new_visit = (loginData == null && now - lastVisit > 1800) + return state } async function main() { offchain = await getOffchainState() + localStorage.setItem('lastVisit', Date.now() / 1000) if (!window.Intl) { require.ensure(['intl/dist/Intl'], (require) => { window.IntlPolyfill = window.Intl = require('intl/dist/Intl') diff --git a/src/app/components/App.scss b/src/app/components/App.scss index 34203b6ad..c0067cd3f 100644 --- a/src/app/components/App.scss +++ b/src/app/components/App.scss @@ -23,17 +23,30 @@ margin-top: 0; } +@keyframes bannerIn { + 0% { + margin-bottom: -28.35em; + transform: translateY(-28.35em); + } + 100% { + margin-bottom: 0em; + transform: translateY(0em); + } +} + .welcomeWrapper { margin-top: -1rem; padding-bottom: 1rem; } .welcomeBanner { - padding: 2.35rem 0 4em; + padding: 2.35em 0 4em; background-color: #fafbfd; background-image: url(../images/lp-bottom.jpg); background-repeat: repeat-x; background-position:bottom; //background-attachment:fixed; + animation: 1s cubic-bezier(.8, .5, .2, 1.4) bannerIn; + z-index: 10; } .RightMenu { diff --git a/src/server/api/general.js b/src/server/api/general.js index 4068fe8cf..064766cf2 100644 --- a/src/server/api/general.js +++ b/src/server/api/general.js @@ -55,7 +55,6 @@ export default function useGeneralApi(app) { const offchain = { csrf: ctx.csrf, flash: ctx.flash, - new_visit: ctx.session.new_visit, account: ctx.session.a, config: $STM_Config, uid: ctx.session.uid, diff --git a/src/server/app_render.jsx b/src/server/app_render.jsx index 36a9f94cc..39f5b2be7 100644 --- a/src/server/app_render.jsx +++ b/src/server/app_render.jsx @@ -23,8 +23,6 @@ async function appRender(ctx) { const offchain = { csrf: ctx.csrf, flash: ctx.flash, - new_visit: ctx.session.new_visit, - account: ctx.session.a, config: $STM_Config, uid: ctx.session.uid, login_challenge diff --git a/src/server/server.js b/src/server/server.js index 1eb4b583f..45e971dbe 100644 --- a/src/server/server.js +++ b/src/server/server.js @@ -205,10 +205,8 @@ app.use(function*(next) { const from_link = this.request.headers.referer; if (!this.session.uid) { this.session.uid = secureRandom.randomBuffer(13).toString('hex'); - this.session.new_visit = true; if (from_link) this.session.r = from_link; } else { - this.session.new_visit = this.session.last_visit - last_visit > 1800; if (!this.session.r && from_link) { this.session.r = from_link; } -- GitLab From dca06d8c2c078c7dff4f0a95e27dc192aebacf2d Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Thu, 12 Oct 2017 16:42:53 +0200 Subject: [PATCH 014/399] Remove unused signup modal Also removes the unused signup_bonus state --- src/app/components/App.jsx | 4 +- src/app/components/all.scss | 1 - src/app/components/modules/Modals.jsx | 16 +---- src/app/components/modules/SignUp.jsx | 87 -------------------------- src/app/components/modules/SignUp.scss | 31 --------- src/app/components/pages/Post.jsx | 6 +- src/server/api/general.js | 1 - src/shared/UniversalRender.jsx | 13 ---- 8 files changed, 5 insertions(+), 154 deletions(-) delete mode 100644 src/app/components/modules/SignUp.jsx delete mode 100644 src/app/components/modules/SignUp.scss diff --git a/src/app/components/App.jsx b/src/app/components/App.jsx index c97aafb40..bbe913ae1 100644 --- a/src/app/components/App.jsx +++ b/src/app/components/App.jsx @@ -131,7 +131,7 @@ class App extends React.Component { render() { const {location, params, children, flash, new_visitor, - depositSteem, signup_bonus, username} = this.props; + depositSteem, username} = this.props; const lp = false; //location.pathname === '/'; const miniHeader = location.pathname === '/create_account' || location.pathname === '/pick_account'; const params_keys = Object.keys(params); @@ -303,7 +303,6 @@ App.propTypes = { error: React.PropTypes.string, children: AppPropTypes.Children, location: React.PropTypes.object, - signup_bonus: React.PropTypes.string, loginUser: React.PropTypes.func.isRequired, depositSteem: React.PropTypes.func.isRequired, username: React.PropTypes.string, @@ -314,7 +313,6 @@ export default connect( return { error: state.app.get('error'), flash: state.offchain.get('flash'), - signup_bonus: state.offchain.get('signup_bonus'), new_visitor: !state.user.get('current') && !state.offchain.get('user') && !state.offchain.get('account') && diff --git a/src/app/components/all.scss b/src/app/components/all.scss index fbdfc884b..d2d7a330f 100644 --- a/src/app/components/all.scss +++ b/src/app/components/all.scss @@ -38,7 +38,6 @@ @import "./modules/Footer"; @import "./modules/lp/LpHeader"; @import "./modules/lp/LpFooter"; -@import "./modules/SignUp"; @import "./modules/LoginForm"; @import "./modules/SidePanel"; @import "./modules/BottomPanel"; diff --git a/src/app/components/modules/Modals.jsx b/src/app/components/modules/Modals.jsx index 511499974..19c07f300 100644 --- a/src/app/components/modules/Modals.jsx +++ b/src/app/components/modules/Modals.jsx @@ -5,7 +5,6 @@ import Reveal from 'react-foundation-components/lib/global/reveal'; import LoginForm from 'app/components/modules/LoginForm'; import ConfirmTransactionForm from 'app/components/modules/ConfirmTransactionForm'; import Transfer from 'app/components/modules/Transfer'; -import SignUp from 'app/components/modules/SignUp'; import user from 'app/redux/User'; import Powerdown from 'app/components/modules/Powerdown'; import tr from 'app/redux/Transaction'; @@ -20,11 +19,9 @@ class Modals extends React.Component { show_confirm_modal: React.PropTypes.bool, show_transfer_modal: React.PropTypes.bool, show_powerdown_modal: React.PropTypes.bool, - show_signup_modal: React.PropTypes.bool, show_promote_post_modal: React.PropTypes.bool, hideLogin: React.PropTypes.func.isRequired, hideConfirm: React.PropTypes.func.isRequired, - hideSignUp: React.PropTypes.func.isRequired, hideTransfer: React.PropTypes.func.isRequired, hidePowerdown: React.PropTypes.func.isRequired, hidePromotePost: React.PropTypes.func.isRequired, @@ -40,8 +37,8 @@ class Modals extends React.Component { render() { const { - show_login_modal, show_confirm_modal, show_transfer_modal, show_powerdown_modal, show_signup_modal, - hideLogin, hideTransfer, hidePowerdown, hideConfirm, hideSignUp, show_terms_modal, + show_login_modal, show_confirm_modal, show_transfer_modal, show_powerdown_modal, + hideLogin, hideTransfer, hidePowerdown, hideConfirm, show_terms_modal, notifications, removeNotification, hidePromotePost, show_promote_post_modal } = this.props; @@ -67,10 +64,6 @@ class Modals extends React.Component { } - {show_signup_modal && - - - } {show_terms_modal && } @@ -92,7 +85,6 @@ export default connect( show_transfer_modal: state.user.get('show_transfer_modal'), show_powerdown_modal: state.user.get('show_powerdown_modal'), show_promote_post_modal: state.user.get('show_promote_post_modal'), - show_signup_modal: state.user.get('show_signup_modal'), notifications: state.app.get('notifications'), show_terms_modal: state.user.get('show_terms_modal') } @@ -118,10 +110,6 @@ export default connect( if (e) e.preventDefault(); dispatch(user.actions.hidePromotePost()) }, - hideSignUp: e => { - if (e) e.preventDefault(); - dispatch(user.actions.hideSignUp()) - }, // example: addNotification: ({key, message}) => dispatch({type: 'ADD_NOTIFICATION', payload: {key, message}}), removeNotification: (key) => dispatch({type: 'REMOVE_NOTIFICATION', payload: {key}}) }) diff --git a/src/app/components/modules/SignUp.jsx b/src/app/components/modules/SignUp.jsx deleted file mode 100644 index 0acbcab80..000000000 --- a/src/app/components/modules/SignUp.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import {connect} from 'react-redux'; -import SvgImage from 'app/components/elements/SvgImage'; -import AddToWaitingList from 'app/components/modules/AddToWaitingList'; - -class SignUp extends React.Component { - constructor() { - super(); - this.state = {waiting_list: false}; - } - render() { - if ($STM_Config.read_only_mode) { - return
    -
    -
    -

    Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.

    -
    -
    ; - } - - if (this.props.serverBusy || $STM_Config.disable_signups) { - return
    -
    -

    Membership to Steemit.com is now under invitation only because of unexpectedly high sign up rate. - Submit your email to get on the waiting list.

    - -
    -
    ; - } - - return
    -
    -
    -

    Sign Up

    -

    Steemit funds each account with over {this.props.signup_bonus} worth of Steem Power; to prevent abuse, we - require new users to login via social media.
    - Your personal information will be kept private. -

    -
    -
    -
    -
    - -
    - -
    -
    -   -
    -
    -
    - -
    -
    - Continue with Reddit -
    (requires 5 or more Reddit comment karma) -
    -
    -
    -
    -
    - Don't have a Facebook or Reddit account?
    - {this.state.waiting_list ? : this.setState({waiting_list: true})}> - Subscribe to get a notification when SMS confirmation is available. - } -
    -
    -
    -
    -
    -

    By verifying your account you agree to the Steemit terms and conditions.

    -
    -
    -
    - } -} - -export default connect( - state => { - return { - signup_bonus: state.offchain.get('signup_bonus'), - serverBusy: state.offchain.get('serverBusy') - }; - } -)(SignUp); diff --git a/src/app/components/modules/SignUp.scss b/src/app/components/modules/SignUp.scss deleted file mode 100644 index bb5f2971a..000000000 --- a/src/app/components/modules/SignUp.scss +++ /dev/null @@ -1,31 +0,0 @@ -.SignUp { - padding: 1rem 1rem 2rem 1rem; - a.button { - margin-top: 0.5rem; - } - .button { - width: 16rem; - } -} - -.SignUp--reddit-button { - background-color: #ef4623; - &:hover { - background-color: scale-color(#ef4623, -6%); - } -} - -.SignUp--fb-button { - background-color: #3b5998; - &:hover { - background-color: scale-color(#3b5998, -6%); - } -} - -.TermsAgree { - height: 10em; - resize: none; - display: block; - overflow: auto; - border: 1px solid #cacaca; -} diff --git a/src/app/components/pages/Post.jsx b/src/app/components/pages/Post.jsx index 5ca116abf..0964a96ae 100644 --- a/src/app/components/pages/Post.jsx +++ b/src/app/components/pages/Post.jsx @@ -21,7 +21,6 @@ class Post extends React.Component { post: React.PropTypes.string, routeParams: React.PropTypes.object, location: React.PropTypes.object, - signup_bonus: React.PropTypes.string, current_user: React.PropTypes.object, }; constructor() { @@ -60,7 +59,7 @@ class Post extends React.Component { render() { const {showSignUp} = this - const {current_user, signup_bonus, content} = this.props + const {current_user, content} = this.props const {showNegativeComments, commentHidden, showAnyway} = this.state let post = this.props.post; if (!post) { @@ -165,7 +164,7 @@ class Post extends React.Component {
    {tt('g.next_7_strings_single_block.authors_get_paid_when_people_like_you_upvote_their_post')}. -
    {tt('g.next_7_strings_single_block.if_you_enjoyed_what_you_read_earn_amount', {amount: '$'+localizedCurrency(signup_bonus.substring(1)), INVEST_TOKEN_UPPERCASE})} +
    {tt('g.next_7_strings_single_block.if_you_enjoyed_what_you_read_earn_amount', {INVEST_TOKEN_UPPERCASE})}
    @@ -200,7 +199,6 @@ export default connect(state => { } return { content: state.global.get('content'), - signup_bonus: state.offchain.get('signup_bonus'), current_user, ignoring, } diff --git a/src/server/api/general.js b/src/server/api/general.js index 064766cf2..7343f0bc3 100644 --- a/src/server/api/general.js +++ b/src/server/api/general.js @@ -59,7 +59,6 @@ export default function useGeneralApi(app) { config: $STM_Config, uid: ctx.session.uid, serverBusy: false, - signup_bonus: '$1', // TODO: don't hardcode this login_challenge }; if (ctx.session.arec) { diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index 4b8c990ae..22edfd389 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -167,20 +167,7 @@ async function universalRender({ location, initial_state, offchain, ErrorPage, t }; } } - // Calculate signup bonus - const fee = parseFloat($STM_Config.registrar_fee.split(' ')[0]), - {base, quote} = onchain.feed_price, - feed = parseFloat(base.split(' ')[0]) / parseFloat(quote.split(' ')[0]); - const sd = fee * feed; - let sdDisp; - if (sd < 1.0) { - sdDisp = '¢' + parseInt(sd * 100); - } else { - const sdInt = parseInt(sd), sdDec = (sd - sdInt); - sdDisp = '$' + sdInt + (sdInt < 5 && sdDec >= 0.5 ? '.50' : ''); - } - offchain.signup_bonus = sdDisp; offchain.server_location = location; server_store = createStore(rootReducer, { global: onchain, offchain}); server_store.dispatch({type: '@@router/LOCATION_CHANGE', payload: {pathname: location}}); -- GitLab From 5a4670730615e5bb574b52559b396c9fb519f4c0 Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Thu, 12 Oct 2017 21:27:38 +0200 Subject: [PATCH 015/399] Allow more than the state api to initiate a session --- src/server/api/account_recovery.js | 1 + src/server/api/general.js | 1 + src/server/api/oauth.js | 2 ++ src/server/server.js | 5 +++-- src/server/sign_up_pages/enter_confirm_email.jsx | 3 +++ src/server/sign_up_pages/enter_confirm_mobile.jsx | 2 ++ 6 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/server/api/account_recovery.js b/src/server/api/account_recovery.js index cc94eca5d..954fe373e 100644 --- a/src/server/api/account_recovery.js +++ b/src/server/api/account_recovery.js @@ -32,6 +32,7 @@ export default function useAccountRecoveryApi(app) { }); router.get('/account_recovery_confirmation/:code', function *() { + this.setCookies = true; if (rateLimitReq(this, this.req)) return; const code = this.params.code; if (!code) return this.throw('no confirmation code', 404); diff --git a/src/server/api/general.js b/src/server/api/general.js index 7343f0bc3..22a62c3f4 100644 --- a/src/server/api/general.js +++ b/src/server/api/general.js @@ -46,6 +46,7 @@ export default function useGeneralApi(app) { const koaBody = koa_body(); router.get('/state', function *() { + this.setCookies = true; const ctx = this; let login_challenge = ctx.session.login_challenge; if (!login_challenge) { diff --git a/src/server/api/oauth.js b/src/server/api/oauth.js index df1a2dbbf..5b914f0ac 100644 --- a/src/server/api/oauth.js +++ b/src/server/api/oauth.js @@ -41,6 +41,7 @@ function retrieveFacebookUserData(access_token) { } function* handleFacebookCallback() { + this.setCookies = true; console.log('-- /handle_facebook_callback -->', this.session.uid, this.query); let email = null; try { @@ -179,6 +180,7 @@ function retrieveRedditUserData(access_token) { } function* handleRedditCallback() { + this.setCookies = true; try { const u = yield retrieveRedditUserData(this.query.access_token); console.log('-- /handle_reddit_callback -->', this.session.uid, u); diff --git a/src/server/server.js b/src/server/server.js index 45e971dbe..9c9764bbb 100644 --- a/src/server/server.js +++ b/src/server/server.js @@ -49,10 +49,11 @@ const numProcesses = process.env.NUM_PROCESSES || os.cpus().length; app.use(requestTime(numProcesses)); -// drop set-cookie headers for everything but state api +// only use set-cookie headers for specific apis +// (they bust the cache) app.use(function*(next) { yield* next; - if (this.request.url !== '/api/v1/state') { + if (this.setCookies !== true) { delete this.response.remove('set-cookie'); } }); diff --git a/src/server/sign_up_pages/enter_confirm_email.jsx b/src/server/sign_up_pages/enter_confirm_email.jsx index e28d6ce72..7d9e1bdd7 100644 --- a/src/server/sign_up_pages/enter_confirm_email.jsx +++ b/src/server/sign_up_pages/enter_confirm_email.jsx @@ -125,6 +125,7 @@ export default function useEnterAndConfirmEmailPages(app) { const rc_site_key = config.get("recaptcha.site_key"); router.get("/start/:code", function*() { + this.setCookies = true; const code = this.params.code; const eid = yield models.Identity.findOne({ attributes: ["id", "user_id", "verified"], where: { provider: "email", confirmation_code: code }}); const user = eid ? yield models.User.findOne({ @@ -180,6 +181,7 @@ export default function useEnterAndConfirmEmailPages(app) { }); router.get("/enter_email", function*() { + this.setCookies = true; console.log("-- /enter_email -->", this.session.uid, this.session.user, this.request.query.account); const picked_account_name = this.session.picked_account_name = this.request.query.account; if (!picked_account_name) { @@ -366,6 +368,7 @@ export default function useEnterAndConfirmEmailPages(app) { router.get("/confirm_email/:code", confirmEmailHandler); router.post("/confirm_email", koaBody, confirmEmailHandler); router.get("/enter_email/submit_form.js", function*() { + this.setCookies = true; this.type = 'application/javascript'; this.body = "function submit_email_form(){document.getElementById('submit_email').submit()}"; }); diff --git a/src/server/sign_up_pages/enter_confirm_mobile.jsx b/src/server/sign_up_pages/enter_confirm_mobile.jsx index 42ef1229c..dff98a991 100644 --- a/src/server/sign_up_pages/enter_confirm_mobile.jsx +++ b/src/server/sign_up_pages/enter_confirm_mobile.jsx @@ -39,6 +39,7 @@ const assets = Object.assign({}, require(assets_file), { script: [] }); // } function* confirmMobileHandler(e) { + this.setCookies = true; if (!checkCSRF(this, this.request.body.csrf)) return; const confirmation_code = this.params && this.params.code ? this.params.code @@ -108,6 +109,7 @@ export default function useEnterAndConfirmMobilePages(app) { const koaBody = koa_body(); router.get("/enter_mobile", function*() { + this.setCookies = true; console.log( "-- /enter_mobile -->", this.session.uid, -- GitLab From 9ac056d13198470753b2e3f892649209dd59f954 Mon Sep 17 00:00:00 2001 From: TimCliff Date: Wed, 25 Oct 2017 19:06:25 -0500 Subject: [PATCH 016/399] Update BadActorList.js --- src/app/utils/BadActorList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index 62d9977ee..402d0cc8e 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -24,6 +24,7 @@ bitrrex bttrex btrex bttrex +bitttex ittrex bittrex-deposit poloiex -- GitLab From 2c3a07fde3f5b6915f7512473fda144cdbd111d8 Mon Sep 17 00:00:00 2001 From: drakos Date: Sat, 28 Oct 2017 14:02:46 -0400 Subject: [PATCH 017/399] Change "People I Follow" to "My Feeds" "People I follow" is confusing, and with the sans font it looks like "People l follow". I suggest "My Feeds" instead; the users were already familiar with that more intuitive notion. --- src/app/components/pages/PostsIndex.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/components/pages/PostsIndex.jsx b/src/app/components/pages/PostsIndex.jsx index 1a9b9edd1..8b499bf02 100644 --- a/src/app/components/pages/PostsIndex.jsx +++ b/src/app/components/pages/PostsIndex.jsx @@ -110,10 +110,10 @@ class PostsIndex extends React.Component { // plus the tag string, f.ex "trending: blog" // // Logged-in: - // At homepage (@user/feed) say "People I follow" + // At homepage (@user/feed) say "My Feeds" let page_title = 'Posts'; // sensible default here? if (typeof this.props.username !== 'undefined' && category === 'feed') { - page_title = 'People I follow'; // todo: localization + page_title = 'My Feeds'; // todo: localization } else { switch (topics_order) { case 'trending': // cribbed from Header.jsx where it's repeated 2x already :P -- GitLab From e583b631022526f4b696560e1831a76a9ac48598 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Tue, 31 Oct 2017 09:36:34 -0500 Subject: [PATCH 018/399] FAQ update - Steem and Steemit logos use --- src/app/help/en/faq.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index ae8765492..3d7efe4f7 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -162,7 +162,7 @@ - Who are the Steemit co-founders? - Can I invest in Steemit? - What does Steemit’s development roadmap look like? -- Am I allowed to use the Steemit logo? +- Am I allowed to use the Steem and Steemit logos? - Can I purchase official Steemit merchandise? - Did Steemit "pre-mine" tokens? - What is the Steemit Privacy Policy? @@ -1217,10 +1217,12 @@ You can view the 2017 Roadmap here: https://steemit.com/steemit/@steemitblog/steemit-2017-roadmap ^ - -## Am I allowed to use the Steemit logo? + +## Am I allowed to use the Steem and Steemit logos? -Currently, the Steem and Steemit logos are the same and is free to use. In the future, Steemit, Inc. will have its own logo so that it can be distinguished from Steem. The Steemit logo will be proprietary while Steem and its three S-shaped squiggles will remain open for public use. +The steemit brand and logo are protected by intellectual property laws including copyright and other proprietary rights of the United States and foreign countries. You may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the steemit logo or brand, except as permitted by the doctrine of fair use or as authorized in writing by us. + +The Steem logo will remain free for anyone to use based on the Creative Commons CC0 license, which makes it safe to use without asking for permission or giving credit, including for commercial purposes. ^ -- GitLab From 808bddd66ea209fdba95ed344ca65b610141709e Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Tue, 31 Oct 2017 10:19:21 -0500 Subject: [PATCH 019/399] language change --- src/app/help/en/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index 3d7efe4f7..367d874a9 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1222,7 +1222,7 @@ https://steemit.com/steemit/@steemitblog/steemit-2017-roadmap The steemit brand and logo are protected by intellectual property laws including copyright and other proprietary rights of the United States and foreign countries. You may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the steemit logo or brand, except as permitted by the doctrine of fair use or as authorized in writing by us. -The Steem logo will remain free for anyone to use based on the Creative Commons CC0 license, which makes it safe to use without asking for permission or giving credit, including for commercial purposes. +The Steem logo should remain free for anyone to use based on the Creative Commons CC0 license, which makes it safe to use without asking for permission or giving credit, including for commercial purposes. ^ -- GitLab From 76893baf28d3bd0131025fbff1890191e368abd6 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Tue, 31 Oct 2017 10:23:55 -0500 Subject: [PATCH 020/399] language update 2 --- src/app/help/en/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index 367d874a9..254482d16 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1220,7 +1220,7 @@ https://steemit.com/steemit/@steemitblog/steemit-2017-roadmap ## Am I allowed to use the Steem and Steemit logos? -The steemit brand and logo are protected by intellectual property laws including copyright and other proprietary rights of the United States and foreign countries. You may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the steemit logo or brand, except as permitted by the doctrine of fair use or as authorized in writing by us. +The steemit brand and logo are protected by intellectual property laws including copyright and other proprietary rights of the United States and foreign countries. One may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the steemit logo or brand, except as permitted by the doctrine of fair use or as authorized in writing by us. The Steem logo should remain free for anyone to use based on the Creative Commons CC0 license, which makes it safe to use without asking for permission or giving credit, including for commercial purposes. -- GitLab From 6451caa163b92d3c19af062613117fbcfee544d9 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Thu, 2 Nov 2017 18:17:14 -0500 Subject: [PATCH 021/399] Update faq.md Proposed IP language changes for @sneak or @nedsteem to review --- src/app/help/en/faq.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index 721e0ec2f..cec205733 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1217,9 +1217,13 @@ https://steemit.com/steemit/@steemitblog/steemit-2017-roadmap ## Am I allowed to use the Steem and Steemit logos? -The steemit brand and logo are protected by intellectual property laws including copyright and other proprietary rights of the United States and foreign countries. One may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the steemit logo or brand, except as permitted by the doctrine of fair use or as authorized in writing by us. +The Steemit brand and logo are protected by intellectual property laws, including copyright and other proprietary rights of the United States and other countries. The purpose is to allow Steemit to protect the brand and logo in ways that extend user safety. One may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the Steemit logo or brand, except as permitted by the doctrine of fair use, or as authorized in writing by us. -The Steem logo should remain free for anyone to use based on the Creative Commons CC0 license, which makes it safe to use without asking for permission or giving credit, including for commercial purposes. +[Section 107 of the Copyright Act](http://www.copyright.gov/title17/92chap1.html#107) provides the statutory framework for determining whether something is a fair use and identifies certain types of uses—such as criticism, comment, news reporting, teaching, scholarship, and research—as examples of activities that may qualify as fair use. **Steemit considers artistic variations of the Steemit logo that are used for non-commercial purposes, and are not used to harm Steemit users (i.e. through attraction to phishing sites), an instance of fair use.** + +Steemit also considers posts which are created on the Steem blockchain and earn rewards through the blockchain rewards mechanism an authorized form of commercial use. + +The Steem logo is licensed under Creative Commons CC0, meaning you’re free to copy, modify, or distribute (even for commercial purposes) without needing to ask permission or give attribution. ^ -- GitLab From c943910b1709bcc4f27312561519531ced915051 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Mon, 6 Nov 2017 10:26:07 -0500 Subject: [PATCH 022/399] get vote count directly for clarity & to speed up mapStateToProps --- src/app/components/elements/VotesAndComments.jsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/components/elements/VotesAndComments.jsx b/src/app/components/elements/VotesAndComments.jsx index f2d64d66c..b496d3819 100644 --- a/src/app/components/elements/VotesAndComments.jsx +++ b/src/app/components/elements/VotesAndComments.jsx @@ -14,7 +14,7 @@ class VotesAndComments extends React.Component { // Redux connect properties comments: React.PropTypes.number, - post_obj: React.PropTypes.object, + totalVotes: React.PropTypes.number, }; constructor(props) { @@ -23,15 +23,14 @@ class VotesAndComments extends React.Component { } render() { - const {comments, commentsLink, post_obj} = this.props; - const total_votes = post_obj.getIn(['stats', 'total_votes']); + const {comments, commentsLink, totalVotes} = this.props; let comments_tooltip = tt('votesandcomments_jsx.no_responses_yet_click_to_respond'); if (comments > 0) comments_tooltip = tt('votesandcomments_jsx.response_count_tooltip', {count: comments}); return ( - -  {total_votes} + +  {totalVotes} @@ -49,7 +48,7 @@ export default connect( if (!post) return props; return { ...props, - post_obj: post, + totalVotes: post.getIn(['stats', 'total_votes']), comments: post.get('children') }; } -- GitLab From b431cf10cb2d1789f0eb73e2ef5c67091cc50452 Mon Sep 17 00:00:00 2001 From: Park Date: Wed, 8 Nov 2017 23:41:18 +0000 Subject: [PATCH 023/399] Add Korean language pack with the minimal set of translation. This work will make the UI usuable enough for the Koreans who do not understand English so that I expect more Korean people will consider to join which will definitely help the growth of Steemit. The entire translation will be done with the Korean community members. Our community will co-work to make the translation as fluent as possible. From @asbear --- src/app/Main.js | 1 + src/app/Translator.js | 6 +- src/app/components/modules/Settings.jsx | 1 + src/app/locales/counterpart/ko.js | 30 ++ src/app/locales/ko.json | 650 ++++++++++++++++++++++++ 5 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 src/app/locales/counterpart/ko.js create mode 100644 src/app/locales/ko.json diff --git a/src/app/Main.js b/src/app/Main.js index 7f312f383..6c7e6f094 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -104,6 +104,7 @@ if (!window.Intl) { require('intl/locale-data/jsonp/ru.js'); require('intl/locale-data/jsonp/fr.js'); require('intl/locale-data/jsonp/it.js'); + require('intl/locale-data/jsonp/ko.js'); Iso.bootstrap(runApp); }, "IntlBundle"); } diff --git a/src/app/Translator.js b/src/app/Translator.js index 61ca4ebc5..7546d3a44 100644 --- a/src/app/Translator.js +++ b/src/app/Translator.js @@ -6,10 +6,11 @@ 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 {DEFAULT_LANGUAGE} from 'app/client_config'; import tt from 'counterpart'; -addLocaleData([...en, ...es, ...ru, ...fr, ...it]); +addLocaleData([...en, ...es, ...ru, ...fr, ...it, ...ko]); tt.registerTranslations('en', require('counterpart/locales/en')); tt.registerTranslations('en', require('app/locales/en.json')); @@ -26,6 +27,9 @@ 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')); + if (process.env.NODE_ENV === 'production') { tt.setFallbackLocale('en'); } diff --git a/src/app/components/modules/Settings.jsx b/src/app/components/modules/Settings.jsx index f32358c40..3e875faa5 100644 --- a/src/app/components/modules/Settings.jsx +++ b/src/app/components/modules/Settings.jsx @@ -139,6 +139,7 @@ class Settings extends React.Component { +
    diff --git a/src/app/locales/counterpart/ko.js b/src/app/locales/counterpart/ko.js new file mode 100644 index 000000000..a43372846 --- /dev/null +++ b/src/app/locales/counterpart/ko.js @@ -0,0 +1,30 @@ +// The translations in this file are added by default. + +'use strict'; + +module.exports = { + counterpart: { + names: require('date-names/en'), + pluralize: require('pluralizers/en'), + + formats: { + date: { + 'default': '%a, %e %b %Y', + long: '%A, %B %o, %Y', + short: '%b %e' + }, + + time: { + 'default': '%H:%M', + long: '%H:%M:%S %z', + short: '%H:%M' + }, + + datetime: { + 'default': '%a, %e %b %Y %H:%M', + long: '%A, %B %o, %Y %H:%M:%S %z', + short: '%e %b %H:%M' + } + } + } +}; diff --git a/src/app/locales/ko.json b/src/app/locales/ko.json new file mode 100644 index 000000000..dbad83253 --- /dev/null +++ b/src/app/locales/ko.json @@ -0,0 +1,650 @@ +{ + "g": { + "age": "나이", + "amount": "Amount", + "and": "and", + "are_you_sure": "Are you sure?", + "ask": "Ask", + "balance": "Balance", + "balances": "Balances", + "bid": "Bid", + "blog": "블로그", + "browse": "Browse", + "buy": "구매", + "buy_or_sell": "Buy or Sell", + "by": "by", + "cancel": "취소", + "change_password": "비밀번호 변경", + "choose_language": "언어 선택", + "clear": "확인", + "close": "닫기", + "collapse_or_expand": "닫기/펼치기", + "comments": "나의댓글", + "confirm": "확인", + "convert": "변환", + "date": "날자", + "delete": "삭제", + "dismiss": "확인", + "edit": "수정", + "email": "Email", + "feed": "피드", + "follow": "팔로우", + "for": " for ", + "from": " from ", + "go_back": "Back", + "hide": "Hide", + "in": "in", + "in_reply_to": "in reply to", + "insufficient_balance": "잔고 부족", + "invalid_amount": "Invalid amount", + "joined": "Joined", + "loading": "Loading", + "login": "로그인", + "logout": "로그아웃", + "memo": "메모", + "mute": "뮤트", + "new": "최신글", + "newer": "Newer", + "next": "Next", + "no": "No", + "ok": "Ok", + "older": "Older", + "or": "or", + "order_placed": "Order placed", + "password": "비밀번호", + "payouts": "글보상", + "permissions": "권한", + "phishy_message": "Link expanded to plain text; beware of a potential phishing attempt", + "post": "글쓰기", + "post_as": "Post as", + "posts": "Posts", + "powered_up_100": "Powered Up 100%%", + "preview": "Preview", + "previous": "Previous", + "price": "Price", + "print": "Print", + "promote": "홍보", + "promoted": "홍보글", + "re": "RE", + "re_to": "RE: %(topic)s", + "recent_password": "Recent Password", + "receive": "Receive ", + "remove": "Remove", + "remove_vote": "보팅취소", + "replied_to": "replied to %(account)s", + "replies": "받은댓글", + "reply": "댓글달기", + "reply_count": { + "zero": "댓글 없음", + "one": "하나의 댓글", + "other": "%(count)개의 댓글" + }, + "reputation": "Reputation", + "reveal_comment": "Reveal Comment", + "request": "request", + "required": "Required", + "rewards": "수익", + "save": "Save", + "saved": "Saved", + "search": "Search", + "sell": "Sell", + "settings": "설정", + "share_this_post": "Share this post", + "show": "Show", + "sign_in": "로그인", + "sign_up": "회원가입", + "since": "since", + "submit": "Submit", + "power_up": "파워 업", + "submit_a_story": "새글", + "tag": "Tag", + "to": " to ", + "topics": "Topics", + "toggle_nightmode": "야간모드전환", + "all_tags": "All tags", + "transfer": "Transfer ", + "trending_topics": "Trending Topics", + "type": "Type", + "unfollow": "언팔로우", + "unmute": "뮤트취소", + "unknown": "Unknown", + "upvote": "보팅", + "upvote_post": "이 글에 보팅", + "username": "사용자 이름", + "version": "버전", + "vote": "보트", + "votes": "보트", + "wallet": "지갑", + "warning": "경고", + "yes": "예", + "posting": "포스팅", + "owner": "소유자", + "active": "액티브", + "account_not_found": "계정을 찾을 수 없습니다", + "this_is_wrong_password": "잘못된 비밀번호 입니다", + "do_you_need_to": "Do you need to", + "account_name": "Account Name", + "recover_your_account": "recover your account", + "reset_usernames_password": "Reset %(username)s's Password", + "this_will_update_usernames_authtype_key": "This will update %(username)s %(authType)s key", + "passwords_do_not_match": "Passwords do not match", + "you_need_private_password_or_key_not_a_public_key": "You need a private password or key (not a public key)", + "the_rules_of_APP_NAME": { + "one": "The first rule of %(APP_NAME)s is: Do not lose your password.", + "second": "The second rule of %(APP_NAME)s is: Do not lose your password.", + "third": "The third rule of %(APP_NAME)s is: We cannot recover your password.", + "fourth": "The fourth rule: If you can remember the password, it's not secure.", + "fifth": "The fifth rule: Use only randomly-generated passwords.", + "sixth": "The sixth rule: Do not tell anyone your password.", + "seventh": "The seventh rule: Always back up your password." + }, + "recover_password": "Recover Account", + "current_password": "Current Password", + "generated_password": "Generated Password", + "backup_password_by_storing_it": "Back it up by storing in your password manager or a text file", + "enter_account_show_password": "Enter a valid account name to show the password", + "click_to_generate_password": "Click to generate password", + "re_enter_generate_password": "Re-enter Generated Password", + "understand_that_APP_NAME_cannot_recover_password": "I understand that %(APP_NAME)s cannot recover lost passwords", + "i_saved_password": "I have securely saved my generated password", + "update_password": "Update Password", + "confirm_password": "Confirm Password", + "account_updated": "Account Updated", + "password_must_be_characters_or_more": "Password must be %(amount)s characters or more", + "need_password_or_key": "You need a private password or key (not a public key)", + "login_to_see_memo": "login to see memo", + "new_password": "New Password", + "incorrect_password": "Incorrect password", + "username_does_not_exist": "Username does not exist", + "account_name_should_start_with_a_letter": "Account name should start with a letter.", + "account_name_should_be_shorter": "Account name should be shorter.", + "account_name_should_be_longer": "Account name should be longer.", + "account_name_should_have_only_letters_digits_or_dashes": "Account name should have only letters, digits, or dashes.", + "cannot_increase_reward_of_post_within_the_last_minute_before_payout": "Cannot increase reward of post within the last minute before payout", + "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": "vote currently exists, user must be indicate a desire to reject witness", + "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": "Only one Steem account allowed per IP address every 10 minutes", + "resteem_this_post": "Resteem This Post", + "reblog": "Resteem", + "write_your_story": "Write your story", + "remember_voting_and_posting_key": "Remember voting & posting key", + "auto_login_question_mark": "Auto login?", + "hide_private_key": "Hide private key", + "show_private_key": "Show private key", + "login_to_show": "Login to show", + "not_valid_email": "Not valid email", + "thank_you_for_being_an_early_visitor_to_APP_NAME": "Thank you for being an early visitor to %(APP_NAME)s. We will get back to you at the earliest possible opportunity.", + "author_rewards": "저자 수익", + "curation_rewards": "큐레이션 수익", + "sorry_your_reddit_account_doesnt_have_enough_karma": "Sorry, your Reddit account doesn't have enough Reddit Karma to qualify for a free sign up. Please add your email for a place on the waiting list", + "register_with_facebook": "Register with Facebook", + "or_click_the_button_below_to_register_with_facebook": "Or click the button below to register with Facebook", + "server_returned_error": "server returned error", + "APP_NAME_support": "%(APP_NAME)s Support", + "please_email_questions_to": "Please email your questions to", + "next_7_strings_single_block": { + "authors_get_paid_when_people_like_you_upvote_their_post": "Authors get paid when people like you upvote their post", + "if_you_enjoyed_what_you_read_earn_amount": "If you enjoyed what you read here, create your account today and start earning FREE STEEM!", + "free_steem": "FREE STEEM!", + "sign_up_earn_steem": "Sign up now to earn " + }, + "next_3_strings_together": { + "show_more": "Show more", + "show_less": "Show fewer", + "value_posts": "low value posts" + }, + "read_only_mode": "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", + "tags_and_topics": "태그", + "show_more_topics": "모든 태그 보기", + "basic": "Basic", + "advanced": "Advanced", + "views": { + "zero": "No Views", + "one": "%(count)s View", + "other": "%(count)s Views" + }, + "responses": { + "zero": "No Responses", + "one": "%(count)s Response", + "other": "%(count)s Responses" + }, + "post_key_warning": { + "confirm": "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", + "warning": "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", + "checkbox": "I understand" + } + }, + "navigation": { + "about": "About", + "explore": "Explore", + "APP_NAME_whitepaper": "%(APP_NAME)s Whitepaper", + "buy_LIQUID_TOKEN": "Buy %(LIQUID_TOKEN)s", + "sell_LIQUID_TOKEN": "Sell %(LIQUID_TOKEN)s", + "currency_market": "Currency Market", + "stolen_account_recovery": "Stolen Accounts Recovery", + "change_account_password": "Change Account Password", + "witnesses": "Witnesses", + "vote_for_witnesses": "Vote for Witnesses", + "privacy_policy": "Privacy Policy", + "terms_of_service": "Terms of Service", + "sign_up": "Join", + "learn_more": "Learn More", + "welcome": "Welcome", + "faq": "FAQ", + "shop": "The Steemit Shop", + "chat": "Steemit Chat", + "app_center": "Steemit App Center", + "api_docs": "Steemit API Docs", + "bluepaper": "Steem Bluepaper", + "whitepaper": "Steem Whitepaper", + "intro_tagline": "머니 톡스", + "intro_paragraph": "Your voice is worth something. Join the community that pays you to post and curate high quality content." + }, + "main_menu": { + "hot": "인기글", + "trending": "추천글" + }, + "reply_editor": { + "shorten_title": "Shorten title", + "exceeds_maximum_length": "Exceeds maximum length (%(maxKb)sKB)", + "including_the_category": " (including the category '%(rootCategory)s')", + "use_limited_amount_of_tags": "You have %(tagsLength)s tags total%(includingCategory)s. Please use only 5 in your post and category line.", + "are_you_sure_you_want_to_clear_this_form": "Are you sure you want to clear this form?", + "uploading": "Uploading", + "draft_saved": "Draft saved.", + "editor": "Editor", + "insert_images_by_dragging_dropping": "이미지 추가를 위해서는 드래그&드롭, ", + "pasting_from_the_clipboard": "클립보드에서 분여넣기, ", + "selecting_them": "혹은 직접 업로드 하세요.", + "image_upload": "사진 업로드", + "power_up_100": "100%% 스팀파워로 받음", + "default_50_50": "스팀파워 50%% + 스팀달러 50%%)", + "decline_payout": "수익을 스팀 커뮤니티에 기부", + "check_this_to_auto_upvote_your_post": "Check this to auto-upvote your post", + "markdown_styling_guide": "Markdown Styling Guide", + "or_by": "or by", + "title": "Title", + "update_post": "Update Post", + "markdown_not_supported": "Markdown is not supported here" + }, + "category_selector_jsx": { + "tag_your_story": "최대 5개의 태그를 입력하세요. 메인 태그는 가장 먼저 입력하세요.", + "select_a_tag": "태그를 선택하세요.", + "maximum_tag_length_is_24_characters": "태그는 24 글자까지 가능합니다.", + "use_limited_amount_of_categories": "태그는 최대 %(amount)s 개 까지 입력 가능합니다.", + "use_only_lowercase_letters": "소문자만 사용해 주세요.", + "use_one_dash": "대쉬는 한개만 사용 가능합니다.", + "use_spaces_to_separate_tags": "태그와 태그 사이는 공백으로 구분 하세요.", + "use_only_allowed_characters": "소문자, 숫자 그리고 대쉬만 사용 가능합니다.", + "must_start_with_a_letter": "태그는 반드시 문자로 시작해야 합니다.", + "must_end_with_a_letter_or_number": "태그는 반드시 문자나 숫자로 끝나야 합니다." + }, + "postfull_jsx": { + "this_post_is_not_available_due_to_a_copyright_claim": "This post is not available due to a copyright claim.", + "share_on_facebook": "Share on Facebook", + "share_on_twitter": "Share on Twitter", + "share_on_linkedin": "Share on Linkedin", + "recent_password": "Recent Password", + "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": "In 3.5 days, convert %(amount)s %(DEBT_TOKEN)s into %(LIQUID_TOKEN)s", + "view_the_full_context": "View the full context", + "view_the_direct_parent": "View the direct parent", + "you_are_viewing_a_single_comments_thread_from": "You are viewing a single comment's thread from" + }, + "market_jsx": { + "action": "Action", + "date_created": "Date Created", + "last_price": "Last price", + "24h_volume": "24h volume", + "spread": "Spread", + "total": "Total", + "available": "Available", + "lowest_ask": "Lowest ask", + "highest_bid": "Highest bid", + "buy_orders": "Buy Orders", + "sell_orders": "Sell Orders", + "trade_history": "Trade History", + "open_orders": "Open Orders", + "sell_amount_for_atleast": "Sell %(amount_to_sell)s for at least %(min_to_receive)s (%(effectivePrice)s)", + "buy_atleast_amount_for": "Buy at least %(min_to_receive)s for %(amount_to_sell)s (%(effectivePrice)s)", + "price_warning_above": "This price is well above the current market price of %(marketPrice)s, are you sure?", + "price_warning_below": "This price is well below the current market price of %(marketPrice)s, are you sure?", + "order_cancel_confirm": "Cancel order %(order_id)s from %(user)s?", + "order_cancelled": "Order %(order_id)s cancelled.", + "higher": "Higher", + "lower": "Lower", + "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" + }, + "recoveraccountstep1_jsx": { + "begin_recovery": "Begin Recovery", + "not_valid": "Not valid", + "account_name_is_not_found": "Account name is not found", + "unable_to_recover_account_not_change_ownership_recently": "We are unable to recover this account, it has not changed ownership recently.", + "password_not_used_in_last_days": "This password was not used on this account in the last 30 days.", + "request_already_submitted_contact_support": "Your request has been already submitted and we are working on it. Please contact %(SUPPORT_EMAIL)s for the status of your request.", + "recover_account_intro": "From time to time, a Steemian's owner key may be compromised. Stolen Account Recovery gives the rightful account owner 30 days to recover their account from the moment the thief changed their owner key. Stolen Account Recovery can only be used on %(APP_URL)s if the account owner had previously listed '%(APP_NAME)s' as their account trustee and complied with %(APP_NAME)s's Terms of Service.", + "login_with_facebook_or_reddit_media_to_verify_identity": "Please login with Facebook or Reddit to verify your identity", + "login_with_social_media_to_verify_identity": "Please login with %(provider)s to verify your identity", + "enter_email_toverify_identity": "We need to verify your identity. Please enter your email address below to begin the verification.", + "continue_with_email": "Continue with Email", + "thanks_for_submitting_request_for_account_recovery": "Thanks for submitting your request for Account Recovery using %(APP_NAME)s's blockchain-based multi factor authentication. We will respond to you as quickly as possible, however, please expect there may be some delay in response due to high volume of emails. Please be prepared to verify your identity.", + "recovering_account": "Recovering account", + "recover_account": "Recover Account", + "checking_account_owner": "Checking account owner", + "sending_recovery_request": "Sending recovery request", + "cant_confirm_account_ownership": "We can't confirm account ownership. Check your password", + "account_recovery_request_not_confirmed": "Account recovery request is not confirmed yet, please get back later, thank you for your patience." + }, + "user_profile": { + "unknown_account": "Unknown Account", + "user_hasnt_made_any_posts_yet": "Looks like %(name)s hasn't made any posts yet!", + "user_hasnt_started_bloggin_yet": "Looks like %(name)s hasn't started blogging yet!", + "user_hasnt_followed_anything_yet": "Looks like %(name)s might not be following anyone yet! If %(name)s recently added new users to follow, their personalized feed will populate once new content is available.", + "user_hasnt_had_any_replies_yet": "%(name)s hasn't had any replies yet", + "looks_like_you_havent_posted_anything_yet": "Looks like you haven't posted anything yet.", + "create_a_post": "Create a Post", + "explore_trending_articles": "Explore Trending Articles", + "read_the_quick_start_guide": "Read The Quick Start Guide", + "browse_the_faq": "Browse The FAQ", + "followers": "팔로워", + "this_is_users_reputations_score_it_is_based_on_history_of_votes": "This is %(name)s's reputation score.\n\nThe reputation score is based on the history of votes received by the account, and is used to hide low quality content.", + "follower_count": { + "zero": "팔로워 없음", + "one": "1명의 팔로워", + "other": "%(count)s명의 팔로워" + }, + "followed_count": { + "zero": "팔로우 없음", + "one": "1명 팔로우", + "other": "%(count)s명 팔로우" + }, + "post_count": { + "zero": "게시글 없음", + "one": "1 게시글", + "other": "%(count)s 게시글" + } + }, + "authorrewards_jsx": { + "estimated_author_rewards_last_week": "금주의 예상 저자 수익", + "author_rewards_history": "저자 수익 내역" + }, + "curationrewards_jsx": { + "estimated_curation_rewards_last_week": "금주의 큐레이션 예상 수익", + "curation_rewards_history": "큐레이션 수익 내역" + }, + "post_jsx": { + "now_showing_comments_with_low_ratings": "Now showing comments with low ratings", + "sort_order": "Sort Order", + "comments_were_hidden_due_to_low_ratings": "Comments were hidden due to low ratings" + }, + "voting_jsx": { + "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": "Flagging a post can remove rewards and make this material less visible. Some common reasons to flag", + "disagreement_on_rewards": "Disagreement on rewards", + "fraud_or_plagiarism": "Fraud or Plagiarism", + "hate_speech_or_internet_trolling": "Hate Speech or Internet Trolling", + "intentional_miss_categorized_content_or_spam": "Intentional miss-categorized content or Spam", + "pending_payout": "대기중인 보상: $%(value)s", + "payout_declined": "보상을 사양함", + "max_accepted_payout": "Max Accepted Payout $%(value)s", + "promotion_cost": "Promotion Cost $%(value)s", + "past_payouts": "Past Payouts $%(value)s", + "past_payouts_author": " - Author $%(value)s", + "past_payouts_curators": " - Curators $%(value)s", + "we_will_reset_curation_rewards_for_this_post": "will reset your curation rewards for this post", + "removing_your_vote": "Removing your vote", + "changing_to_an_upvote": "Changing to an Up-Vote", + "changing_to_a_downvote": "Changing to a Down-Vote", + "confirm_flag": "Confirm Flag", + "and_more": "and %(count)s more", + "votes_plural": { + "one": "%(count)s 보트", + "other": "%(count)s 보트" + } + }, + "witnesses_jsx": { + "witness_thread": "witness thread", + "top_witnesses": "증인 투표", + "you_have_votes_remaining": { + "zero": "You have no votes remaining", + "one": "You have 1 vote remaining", + "other": "You have %(count)s votes remaining" + }, + "you_can_vote_for_maximum_of_witnesses": "You can vote for a maximum of 30 witnesses", + "witness": "증인", + "information": "Information", + "if_you_want_to_vote_outside_of_top_enter_account_name": "If you would like to vote for a witness outside of the top 50, enter the account name below to cast a vote", + "set_witness_proxy": "You can also choose a proxy that will vote for witnesses for you. This will reset your current witness selection.", + "witness_set": "You have set a voting proxy. If you would like to re-enable manual voting, please clear your proxy.", + "witness_proxy_current": "Your current proxy is", + "witness_proxy_set": "Set proxy", + "witness_proxy_clear": "Clear proxy", + "proxy_update_error": "Your proxy was not updated" + }, + "votesandcomments_jsx": { + "no_responses_yet_click_to_respond": "No responses yet. Click to respond.", + "response_count_tooltip": { + "zero": "no responses. Click to respond.", + "one": "1 response. Click to respond.", + "other": "%(count)s responses. Click to respond." + }, + "vote_count": { + "zero": "추천 없음", + "one": "1개의 추천", + "other": "%(count)s개의 추천" + } + }, + "userkeys_jsx": { + "public": "Public", + "private": "Private", + "public_something_key": "Public %(key)s Key", + "private_something_key": "Private %(key)s Key", + "posting_key_is_required_it_should_be_different": "The posting key is used for posting and voting. It should be different from the active and owner keys.", + "the_active_key_is_used_to_make_transfers_and_place_orders": "The active key is used to make transfers and place orders in the internal market.", + "the_owner_key_is_required_to_change_other_keys": "The owner key is the master key for the account and is required to change the other keys.", + "the_private_key_or_password_should_be_kept_offline": "The private key or password for the owner key should be kept offline as much as possible.", + "the_memo_key_is_used_to_create_and_read_memos": "The memo key is used to create and read memos." + }, + "suggestpassword_jsx": { + "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": "%(APP_NAME)s cannot recover passwords. Keep this page in a secure location, such as a fireproof safe or safety deposit box.", + "APP_NAME_password_backup": "%(APP_NAME)s Password Backup", + "APP_NAME_password_backup_required": "%(APP_NAME)s Password Backup (required)", + "after_printing_write_down_your_user_name": "After printing, write down your user name" + }, + "converttosteem_jsx": { + "your_existing_DEBT_TOKEN_are_liquid_and_transferable": "Your existing %(DEBT_TOKEN)s are liquid and transferable. Instead you may wish to trade %(DEBT_TOKEN)s directly in this site under %(link)s or transfer to an external market.", + "this_is_a_price_feed_conversion": "This is a price feed conversion. The 3.5 day delay is necessary to prevent abuse from gaming the price feed average", + "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", + "DEBT_TOKEN_will_be_unavailable": "This action will take place 3.5 days from now and can not be canceled. These %(DEBT_TOKEN)s will immediately become unavailable" + }, + "tips_js": { + "liquid_token": "Tradeable tokens that may be transferred anywhere at anytime.
    %(LIQUID_TOKEN)s can be converted to %(VESTING_TOKEN)s in a process called powering up.", + "influence_token": "Influence tokens which give you more control over post payouts and allow you to earn on curation rewards.", + "estimated_value": "The estimated value is based on an average value of %(LIQUID_TOKEN)s in US dollars.", + "non_transferable": "%(VESTING_TOKEN)s is non-transferable and requires 3 months (13 payments) to convert back to %(LIQUID_TOKEN)s.", + "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": "Converted %(VESTING_TOKEN)s can be sent to yourself or someone else but can not transfer again without converting back to %(LIQUID_TOKEN)s.", + "part_of_your_steem_power_is_currently_delegated": "Part of your STEEM POWER is currently delegated to you. Delegation is donated for influence or to help new users perform actions on steemit. Your delegation amount can fluctuate." + }, + "promote_post_jsx": { + "promote_post": "Promote Post", + "spend_your_DEBT_TOKEN_to_advertise_this_post": "Spend your %(DEBT_TOKEN)s's to advertise this post in the promoted content section", + "you_successfully_promoted_this_post": "You successfully promoted this post", + "this_post_was_hidden_due_to_low_ratings": "This post was hidden due to low ratings" + }, + "about_jsx": { + "about_app": "About %(APP_NAME)s", + "about_app_details": "%(APP_NAME)s is a social media platform where everyone gets paid for creating and curating content. It leverages a robust digital points system, called Steem, that supports real value for digital rewards through market price discovery and liquidity.", + "learn_more_at_app_url": "Learn more at %(APP_URL)s", + "resources": "Resources" + }, + "markdownviewer_jsx": { + "images_were_hidden_due_to_low_ratings": "Images were hidden due to low ratings." + }, + "postsummary_jsx": { + "resteemed": "리스팀", + "resteemed_by": "리스팀 by", + "reveal_it": "Reveal this post", + "adjust_your": "adjust your", + "display_preferences": "display preferences", + "create_an_account": "create an account", + "to_save_your_preferences": "to save your preferences" + }, + "posts_index": { + "empty_feed_1": "Looks like you haven't followed anything yet", + "empty_feed_2": "If you recently added new users to follow, your personalized feed will populate once new content is available", + "empty_feed_3": "Explore Trending Articles", + "empty_feed_4": "Read The Quick Start Guide", + "empty_feed_5": "Browse The FAQ" + }, + "transferhistoryrow_jsx": { + "to_savings": "to savings", + "from_savings": "from savings", + "cancel_transfer_from_savings": "Cancel transfer from savings", + "stop_power_down": "Stop power down", + "start_power_down_of": "Start power down of", + "receive_interest_of": "Receive interest of" + }, + "savingswithdrawhistory_jsx": { + "cancel_this_withdraw_request": "Cancel this withdraw request?", + "pending_savings_withdrawals": "PENDING SAVINGS WITHDRAWS", + "withdraw": "Withdraw %(amount)s", + "to": "to %(to)s", + "from_to": "from %(from)s to %(to)s" + }, + "explorepost_jsx": { + "copied": "Copied!", + "copy": "COPY", + "alternative_sources": "Alternative Sources" + }, + "header_jsx": { + "home": "home", + "create_a_post": "Create a post", + "change_account_password": "Change Account Password", + "create_account": "Create Account", + "stolen_account_recovery": "Stolen Account Recovery", + "people_following": "People following", + "people_followed_by": "People followed by", + "curation_rewards_by": "Curation rewards by", + "author_rewards_by": "Author rewards by", + "replies_to": "Replies to", + "comments_by": "Comments by" + }, + "loginform_jsx": { + "you_need_a_private_password_or_key": "You need a private password or key (not a public key)", + "cryptography_test_failed": "Cryptography test failed", + "unable_to_log_you_in": "We will be unable to log you in with this browser.", + "the_latest_versions_of": "The latest versions of ", + "are_well_tested_and_known_to_work_with": "are well tested and known to work with %(APP_URL)s.", + "due_to_server_maintenance": "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", + "login_to_vote": "Login to Vote", + "login_to_post": "Login to Post", + "login_to_comment": "Login to Comment", + "posting": "Posting", + "active_or_owner": "Active or Owner", + "this_password_is_bound_to_your_account_owner_key": "This password is bound to your account's owner key and can not be used to login to this site.", + "however_you_can_use_it_to": "However, you can use it to ", + "update_your_password": "update your password", + "to_obtain_a_more_secure_set_of_keys": "to obtain a more secure set of keys.", + "this_password_is_bound_to_your_account_active_key": "This password is bound to your account's active key and can not be used to login to this page.", + "you_may_use_this_active_key_on_other_more": "You may use this active key on other more secure pages like the Wallet or Market pages.", + "you_account_has_been_successfully_created": "You account has been successfully created!", + "you_account_has_been_successfully_recovered": "You account has been successfully recovered!", + "password_update_succes": "The password for %(accountName)s was successfully updated", + "password_info": "This password or private key was entered incorrectly. There is probably a handwriting or data-entry error. Hint: A password or private key generated by Steemit will never contain 0 (zero), O (capital o), I (capital i) and l (lower case L) characters.", + "enter_your_username": "Enter your username", + "password_or_wif": "Password or WIF", + "this_operation_requires_your_key_or_master_password": "This operation requires your %(authType)s key or Master password.", + "keep_me_logged_in": "Keep me logged in", + "amazing_community": "amazing community", + "to_comment_and_reward_others": " to comment and reward others.", + "signup_button": "Sign up now to earn ", + "signup_button_emphasis": "FREE STEEM!", + "sign_up_get_steem": "Sign up. Get STEEM", + "returning_users": "Returning Users: ", + "join_our": "Join our" + }, + "chainvalidation_js": { + "account_name_should": "Account name should ", + "not_be_empty": "not be empty.", + "be_longer": "be longer.", + "be_shorter": "be shorter.", + "each_account_segment_should": "Each account segment should ", + "start_with_a_letter": "start with a letter.", + "have_only_letters_digits_or_dashes": "have only letters, digits, or dashes.", + "have_only_one_dash_in_a_row": "have only one dash in a row.", + "end_with_a_letter_or_digit": "end with a letter or digit.", + "verified_exchange_no_memo": "You must include a memo for your exchange transfer." + }, + "settings_jsx": { + "invalid_url": "잘못된 URL 입니다", + "name_is_too_long": "너무 긴 사용자 이름입니다", + "name_must_not_begin_with": "사용자 이름은 반드시 @로 시작해야 합니다", + "about_is_too_long": "About is too long", + "location_is_too_long": "너무 긴 지역명입니다", + "website_url_is_too_long": "너무 긴 웹사이트 URL입니다", + "public_profile_settings": "공개 프로파일 설정", + "private_post_display_settings": "Private Post Display Settings", + "not_safe_for_work_nsfw_content": "Not safe for work (NSFW) content", + "always_hide": "항상 숨기기", + "always_warn": "항상 경고하기", + "always_show": "항상 보이기", + "muted_users": "뮤트한 사용자", + "update": "저장", + "profile_image_url": "프로파일 사진 경로", + "cover_image_url": "커버이미지 사진 경로", + "profile_name": "닉네임", + "profile_about": "한줄 소개", + "profile_location": "지역", + "profile_website": "웹사이트" + }, + "transfer_jsx": { + "amount_is_in_form": "Amount is in the form 99999.999", + "insufficient_funds": "Insufficient funds", + "use_only_3_digits_of_precison": "Use only 3 digits of precison", + "send_to_account": "Send to account", + "asset": "Asset", + "this_memo_is_private": "This memo is private", + "this_memo_is_public": "This memo is public", + "convert_to_VESTING_TOKEN": "Convert to %(VESTING_TOKEN)s", + "balance_subject_to_3_day_withdraw_waiting_period": "Balance subject to 3 day withdraw waiting period,", + "move_funds_to_another_account": "Move funds to another %(APP_NAME)s account.", + "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": "Protect funds by requiring a 3 day withdraw waiting period.", + "withdraw_funds_after_the_required_3_day_waiting_period": "Withdraw funds after the required 3 day waiting period.", + "from": "From", + "to": "To", + "asset_currently_collecting": "%(asset)s currently collecting %(interest)s%% APR.", + "beware_of_spam_and_phishing_links": "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." + }, + "userwallet_jsx": { + "conversion_complete_tip": "Will complete on", + "in_conversion": "%(amount)s in conversion", + "transfer_to_savings": "Transfer to Savings", + "power_up": "Power Up", + "power_down": "Power Down", + "market": "Market", + "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", + "withdraw_LIQUID_TOKEN": "Withdraw %(LIQUID_TOKEN)s", + "withdraw_DEBT_TOKENS": "Withdraw %(DEBT_TOKENS)s", + "tokens_worth_about_1_of_LIQUID_TICKER": "Tokens worth about $1.00 of %(LIQUID_TICKER)s, currently collecting %(sbdInterest)s%% APR.", + "savings": "SAVINGS", + "estimated_account_value": "Estimated Account Value", + "next_power_down_is_scheduled_to_happen": "The next power down is scheduled to happen", + "transfers_are_temporary_disabled": "Transfers are temporary disabled.", + "history": "HISTORY", + "redeem_rewards": "내 지갑으로 입금하기", + "buy_steem_or_steem_power": "STEEM, STEEM POWER 구입" + }, + "powerdown_jsx": { + "power_down": "파워다운", + "amount": "Amount", + "already_power_down": "You are already powering down %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s paid out so far). Note that if you change the power down amount the payout schedule will reset.", + "delegating": "You are delegating %(AMOUNT)s %(LIQUID_TICKER)s. That amount is locked up and not available to power down until the delegation is removed and a full reward period has passed.", + "per_week": "That's ~%(AMOUNT)s %(LIQUID_TICKER)s per week.", + "warning": "Leaving less than %(AMOUNT)s %(VESTING_TOKEN)s in your account is not recommended and can leave your account in a unusable state.", + "error": "Unable to power down (ERROR: %(MESSAGE)s)" + }, + "checkloginowner_jsx": { + "your_password_permissions_were_reduced": "Your password permissions were reduced", + "if_you_did_not_make_this_change": "If you did not make this change please", + "ownership_changed_on": "Ownership Changed On ", + "deadline_for_recovery_is": "Deadline for recovery is", + "i_understand_dont_show_again": "이 메시지를 다시 보지 않기" + } +} -- GitLab From 37184de526abbe8abb35e10974a65a078e267302 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Sun, 12 Nov 2017 18:37:21 +0100 Subject: [PATCH 024/399] added blocktraded to badactors --- src/app/utils/BadActorList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index 62d9977ee..a26fe8feb 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -80,6 +80,7 @@ polonied polonixe blocktardes blocktrade +blocktraded bocktrades changelly.com changely -- GitLab From 5fa62d38d206eae288e24fd039567bdaf025c7ce Mon Sep 17 00:00:00 2001 From: Gandalf Date: Sun, 12 Nov 2017 18:41:21 +0100 Subject: [PATCH 025/399] added blocktrader to bad actors --- src/app/utils/BadActorList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index a26fe8feb..82c067c2b 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -81,6 +81,7 @@ polonixe blocktardes blocktrade blocktraded +blocktrader bocktrades changelly.com changely -- GitLab From 7e269bc0abdd747cafcb6fc8ab876d9db3ce306b Mon Sep 17 00:00:00 2001 From: Gandalf Date: Mon, 13 Nov 2017 07:50:47 +0100 Subject: [PATCH 026/399] two more bad actors --- src/app/utils/BadActorList.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index 82c067c2b..2204ab832 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -78,8 +78,10 @@ polnoiex polonyex polonied polonixe +blockrade blocktardes blocktrade +blocktradees blocktraded blocktrader bocktrades -- GitLab From b9d0d44e0d3bad6d73498c096f53978d17c28fc8 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 14 Nov 2017 10:38:17 +0100 Subject: [PATCH 027/399] FIx redundant Strict-Transport-Security response header --- src/server/server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/server.js b/src/server/server.js index 35729152c..4be2f9150 100644 --- a/src/server/server.js +++ b/src/server/server.js @@ -161,7 +161,9 @@ if (env === 'production') { app.use(koa_logger()); } -app.use(helmet()); +app.use(helmet({ + hsts: false +})); app.use( mount( -- GitLab From ac4b5e7a7f37160c7f94c2c11b53f192b3333e83 Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Tue, 14 Nov 2017 12:32:47 +0100 Subject: [PATCH 028/399] Depend on steem-js via the @steemit/steem-js pkg --- package.json | 2 +- src/app/Main.js | 2 +- src/app/components/App.jsx | 2 +- .../components/elements/ChangePassword.jsx | 4 +- .../elements/GeneratedPasswordInput.jsx | 2 +- src/app/components/elements/KeyEdit.js | 2 +- src/app/components/elements/Memo.js | 2 +- .../components/elements/SuggestPassword.jsx | 2 +- src/app/components/modules/LoginForm.jsx | 2 +- src/app/components/pages/CreateAccount.jsx | 4 +- src/app/components/pages/PickAccount.jsx | 2 +- .../components/pages/RecoverAccountStep1.jsx | 4 +- .../components/pages/RecoverAccountStep2.jsx | 4 +- src/app/redux/AuthSaga.js | 4 +- src/app/redux/FetchDataSaga.js | 2 +- src/app/redux/FollowSaga.js | 2 +- src/app/redux/MarketSaga.js | 2 +- src/app/redux/PollDataSaga.js | 2 +- src/app/redux/SagaShared.js | 2 +- src/app/redux/TransactionSaga.js | 4 +- src/app/redux/UserSaga.js | 4 +- src/app/utils/BrowserTests.js | 4 +- src/app/utils/ChainValidation.js | 2 +- src/app/utils/ConsoleExports.js | 2 +- src/app/utils/ServerApiClient.js | 2 +- src/server/api/account_recovery.js | 2 +- src/server/api/general.js | 4 +- src/server/index.js | 2 +- src/server/json/post_json.jsx | 2 +- src/server/json/user_json.jsx | 2 +- .../sign_up_pages/enter_confirm_email.jsx | 2 +- src/shared/UniversalRender.jsx | 2 +- src/shared/api_client/ChainConfig.js | 2 +- yarn.lock | 44 +++++++++---------- 34 files changed, 64 insertions(+), 64 deletions(-) diff --git a/package.json b/package.json index d07e6adab..0b26a9373 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "sequelize-cli": "^2.3.1", "speakingurl": "^9.0.0", "sqlite3": "^3.1.8", - "steem": "github:steemit/steem-js#72a36c835a74f446da47e36feea5d5f1f4dde55b", + "@steemit/steem-js": "^0.6.5", "store": "^1.3.20", "style-loader": "^0.18.2", "svg-inline-loader": "^0.8.0", diff --git a/src/app/Main.js b/src/app/Main.js index 7f312f383..8dc1e6da3 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -7,7 +7,7 @@ import Iso from 'iso'; import universalRender from 'shared/UniversalRender'; import ConsoleExports from './utils/ConsoleExports'; import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; -import * as steem from 'steem'; +import * as steem from '@steemit/steem-js'; window.onerror = error => { if (window.$STM_csrf) serverApiRecordEvent('client_error', error); diff --git a/src/app/components/App.jsx b/src/app/components/App.jsx index 78741a1ba..de79351f8 100644 --- a/src/app/components/App.jsx +++ b/src/app/components/App.jsx @@ -18,7 +18,7 @@ import tt from 'counterpart'; import PageViewsCounter from 'app/components/elements/PageViewsCounter'; import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; import { APP_NAME, VESTING_TOKEN, LIQUID_TOKEN } from 'app/client_config'; -import {key_utils} from 'steem/lib/auth/ecc'; +import {key_utils} from '@steemit/steem-js/lib/auth/ecc'; import resolveRoute from 'app/ResolveRoute'; const pageRequiresEntropy = (path) => { diff --git a/src/app/components/elements/ChangePassword.jsx b/src/app/components/elements/ChangePassword.jsx index 835d564ed..97302926e 100644 --- a/src/app/components/elements/ChangePassword.jsx +++ b/src/app/components/elements/ChangePassword.jsx @@ -6,8 +6,8 @@ import {validate_account_name} from 'app/utils/ChainValidation' import {cleanReduxInput} from 'app/utils/ReduxForms' import tt from 'counterpart'; import { APP_NAME } from 'app/client_config'; -import {PrivateKey, PublicKey, key_utils} from 'steem/lib/auth/ecc'; -import {api} from 'steem'; +import {PrivateKey, PublicKey, key_utils} from '@steemit/steem-js/lib/auth/ecc'; +import {api} from '@steemit/steem-js'; const {string, oneOf} = React.PropTypes diff --git a/src/app/components/elements/GeneratedPasswordInput.jsx b/src/app/components/elements/GeneratedPasswordInput.jsx index c50f31ce7..a58e8e09f 100644 --- a/src/app/components/elements/GeneratedPasswordInput.jsx +++ b/src/app/components/elements/GeneratedPasswordInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import tt from 'counterpart'; import { APP_NAME } from 'app/client_config'; -import {key_utils} from 'steem/lib/auth/ecc'; +import {key_utils} from '@steemit/steem-js/lib/auth/ecc'; function allChecked(confirmCheckboxes) { return confirmCheckboxes.box1 && confirmCheckboxes.box2; diff --git a/src/app/components/elements/KeyEdit.js b/src/app/components/elements/KeyEdit.js index 0ae7c4068..ec48f7e6a 100644 --- a/src/app/components/elements/KeyEdit.js +++ b/src/app/components/elements/KeyEdit.js @@ -3,7 +3,7 @@ import LoadingIndicator from 'app/components/elements/LoadingIndicator' import {reduxForm} from 'redux-form' // @deprecated, instead use: app/utils/ReactForm.js import {cleanReduxInput} from 'app/utils/ReduxForms' import tt from 'counterpart'; -import {PrivateKey, PublicKey} from 'steem/lib/auth/ecc'; +import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc'; class KeyEdit extends Component { static propTypes = { diff --git a/src/app/components/elements/Memo.js b/src/app/components/elements/Memo.js index f1ac37250..4560d2e8d 100644 --- a/src/app/components/elements/Memo.js +++ b/src/app/components/elements/Memo.js @@ -2,7 +2,7 @@ import React, {PropTypes} from 'react'; import {connect} from 'react-redux'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; import tt from 'counterpart'; -import {memo} from 'steem'; +import {memo} from '@steemit/steem-js'; class Memo extends React.Component { static propTypes = { diff --git a/src/app/components/elements/SuggestPassword.jsx b/src/app/components/elements/SuggestPassword.jsx index a4aed9306..ff6220763 100644 --- a/src/app/components/elements/SuggestPassword.jsx +++ b/src/app/components/elements/SuggestPassword.jsx @@ -7,7 +7,7 @@ import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' import Icon from 'app/components/elements/Icon' import tt from 'counterpart'; import { APP_NAME, APP_ICON } from 'app/client_config'; -import {key_utils} from 'steem/lib/auth/ecc'; +import {key_utils} from '@steemit/steem-js/lib/auth/ecc'; const {bool} = React.PropTypes diff --git a/src/app/components/modules/LoginForm.jsx b/src/app/components/modules/LoginForm.jsx index 2a7d49944..0dde2bf74 100644 --- a/src/app/components/modules/LoginForm.jsx +++ b/src/app/components/modules/LoginForm.jsx @@ -10,7 +10,7 @@ import reactForm from 'app/utils/ReactForm' import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; import tt from 'counterpart'; import { APP_URL } from 'app/client_config'; -import {PrivateKey, PublicKey} from 'steem/lib/auth/ecc'; +import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc'; class LoginForm extends Component { diff --git a/src/app/components/pages/CreateAccount.jsx b/src/app/components/pages/CreateAccount.jsx index 75503ef1e..5cebb74cb 100644 --- a/src/app/components/pages/CreateAccount.jsx +++ b/src/app/components/pages/CreateAccount.jsx @@ -4,12 +4,12 @@ import React from 'react'; import {connect} from 'react-redux'; import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import user from 'app/redux/User'; -import {PrivateKey} from 'steem/lib/auth/ecc'; +import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; import {validate_account_name} from 'app/utils/ChainValidation'; import runTests from 'app/utils/BrowserTests'; import GeneratedPasswordInput from 'app/components/elements/GeneratedPasswordInput'; import {saveCords} from 'app/utils/ServerApiClient'; -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; import { Link } from 'react-router'; class CreateAccount extends React.Component { diff --git a/src/app/components/pages/PickAccount.jsx b/src/app/components/pages/PickAccount.jsx index ce552215e..2ca7ae9bc 100644 --- a/src/app/components/pages/PickAccount.jsx +++ b/src/app/components/pages/PickAccount.jsx @@ -5,7 +5,7 @@ import {connect} from 'react-redux'; import Progress from 'react-foundation-components/lib/global/progress-bar'; import { Link } from 'react-router'; import classNames from 'classnames'; -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; import user from 'app/redux/User'; import {validate_account_name} from 'app/utils/ChainValidation'; import runTests from 'app/utils/BrowserTests'; diff --git a/src/app/components/pages/RecoverAccountStep1.jsx b/src/app/components/pages/RecoverAccountStep1.jsx index 8e89b9b17..59b8a1ca3 100644 --- a/src/app/components/pages/RecoverAccountStep1.jsx +++ b/src/app/components/pages/RecoverAccountStep1.jsx @@ -6,8 +6,8 @@ import constants from 'app/redux/constants'; import tt from 'counterpart'; import { FormattedHTMLMessage } from 'app/Translator'; import { APP_DOMAIN, APP_NAME, SUPPORT_EMAIL } from 'app/client_config'; -import {PrivateKey} from 'steem/lib/auth/ecc'; -import {api} from 'steem'; +import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; +import {api} from '@steemit/steem-js'; const email_regex = /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$/; diff --git a/src/app/components/pages/RecoverAccountStep2.jsx b/src/app/components/pages/RecoverAccountStep2.jsx index c12d3e8ec..085dec679 100644 --- a/src/app/components/pages/RecoverAccountStep2.jsx +++ b/src/app/components/pages/RecoverAccountStep2.jsx @@ -4,8 +4,8 @@ import GeneratedPasswordInput from 'app/components/elements/GeneratedPasswordInp import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import tt from 'counterpart'; import Callout from 'app/components/elements/Callout'; -import {PrivateKey} from 'steem/lib/auth/ecc'; -import {api} from 'steem'; +import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; +import {api} from '@steemit/steem-js'; function passwordToOwnerPubKey(account_name, password) { let pub_key; diff --git a/src/app/redux/AuthSaga.js b/src/app/redux/AuthSaga.js index 990303d07..e80366d96 100644 --- a/src/app/redux/AuthSaga.js +++ b/src/app/redux/AuthSaga.js @@ -3,8 +3,8 @@ import {call, put, select} from 'redux-saga/effects'; import {Set, Map, fromJS, List} from 'immutable' import user from 'app/redux/User' import {getAccount} from 'app/redux/SagaShared' -import {PrivateKey} from 'steem/lib/auth/ecc'; -import {api} from 'steem'; +import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; +import {api} from '@steemit/steem-js'; // operations that require only posting authority const postingOps = Set(`vote, comment, delete_comment, custom_json, claim_reward_balance`.trim().split(/,\s*/)) diff --git a/src/app/redux/FetchDataSaga.js b/src/app/redux/FetchDataSaga.js index c6a9000b8..d15a83805 100644 --- a/src/app/redux/FetchDataSaga.js +++ b/src/app/redux/FetchDataSaga.js @@ -5,7 +5,7 @@ import {getContent} from 'app/redux/SagaShared'; import GlobalReducer from './GlobalReducer'; import constants from './constants'; import {fromJS, Map} from 'immutable' -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; export const fetchDataWatches = [watchLocationChange, watchDataRequests, watchFetchJsonRequests, watchFetchState, watchGetContent]; diff --git a/src/app/redux/FollowSaga.js b/src/app/redux/FollowSaga.js index 2219cc28b..835a573fb 100644 --- a/src/app/redux/FollowSaga.js +++ b/src/app/redux/FollowSaga.js @@ -1,6 +1,6 @@ import {fromJS, Map, Set} from 'immutable' import {call, put, select} from 'redux-saga/effects'; -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; /** This loadFollows both 'blog' and 'ignore' diff --git a/src/app/redux/MarketSaga.js b/src/app/redux/MarketSaga.js index b51b5ce10..a53329e7c 100644 --- a/src/app/redux/MarketSaga.js +++ b/src/app/redux/MarketSaga.js @@ -2,7 +2,7 @@ import {takeLatest} from 'redux-saga'; import {call, put} from 'redux-saga/effects'; import MarketReducer from './MarketReducer'; import {getAccount} from './SagaShared'; -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; export const marketWatches = [watchLocationChange, watchUserLogin, watchMarketUpdate]; diff --git a/src/app/redux/PollDataSaga.js b/src/app/redux/PollDataSaga.js index 5f676d122..d7946d13f 100644 --- a/src/app/redux/PollDataSaga.js +++ b/src/app/redux/PollDataSaga.js @@ -2,7 +2,7 @@ import { call, put, select } from 'redux-saga/effects'; import GlobalReducer from './GlobalReducer'; import {getNotifications, webPushRegister} from 'app/utils/ServerApiClient'; import registerServiceWorker from 'app/utils/RegisterServiceWorker'; -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; const wait = ms => ( new Promise(resolve => { diff --git a/src/app/redux/SagaShared.js b/src/app/redux/SagaShared.js index 7d99ce935..7a1de0cb8 100644 --- a/src/app/redux/SagaShared.js +++ b/src/app/redux/SagaShared.js @@ -3,7 +3,7 @@ import {call, put, select} from 'redux-saga/effects'; import g from 'app/redux/GlobalReducer' import {takeEvery, takeLatest} from 'redux-saga'; import tt from 'counterpart'; -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; import {setUserPreferences} from 'app/utils/ServerApiClient'; const wait = ms => ( diff --git a/src/app/redux/TransactionSaga.js b/src/app/redux/TransactionSaga.js index 5d5246b7f..34c0876c4 100644 --- a/src/app/redux/TransactionSaga.js +++ b/src/app/redux/TransactionSaga.js @@ -10,8 +10,8 @@ import tt from 'counterpart' import getSlug from 'speakingurl' import {DEBT_TICKER} from 'app/client_config' import {serverApiRecordEvent} from 'app/utils/ServerApiClient' -import {PrivateKey, PublicKey} from 'steem/lib/auth/ecc'; -import {api, broadcast, auth, memo} from 'steem'; +import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc'; +import {api, broadcast, auth, memo} from '@steemit/steem-js'; export const transactionWatches = [ watchForBroadcast, diff --git a/src/app/redux/UserSaga.js b/src/app/redux/UserSaga.js index 77bb5854c..f6bb46c1a 100644 --- a/src/app/redux/UserSaga.js +++ b/src/app/redux/UserSaga.js @@ -8,8 +8,8 @@ import {browserHistory} from 'react-router' import {serverApiLogin, serverApiLogout} from 'app/utils/ServerApiClient'; import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; import {loadFollows} from 'app/redux/FollowSaga' -import {PrivateKey, Signature, hash} from 'steem/lib/auth/ecc'; -import {api} from 'steem'; +import {PrivateKey, Signature, hash} from '@steemit/steem-js/lib/auth/ecc'; +import {api} from '@steemit/steem-js'; import {translate} from 'app/Translator'; import DMCAUserList from 'app/utils/DMCAUserList'; diff --git a/src/app/utils/BrowserTests.js b/src/app/utils/BrowserTests.js index ddab1b8e9..876673553 100644 --- a/src/app/utils/BrowserTests.js +++ b/src/app/utils/BrowserTests.js @@ -1,7 +1,7 @@ import assert from 'assert' import {serverApiRecordEvent} from 'app/utils/ServerApiClient' -import {PrivateKey, PublicKey} from 'steem/lib/auth/ecc' -import {config} from 'steem'; +import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc' +import {config} from '@steemit/steem-js'; export const browserTests = {} diff --git a/src/app/utils/ChainValidation.js b/src/app/utils/ChainValidation.js index b3e3417e7..19a01e21a 100644 --- a/src/app/utils/ChainValidation.js +++ b/src/app/utils/ChainValidation.js @@ -1,7 +1,7 @@ import tt from 'counterpart'; import BadActorList from 'app/utils/BadActorList'; import VerifiedExchangeList from 'app/utils/VerifiedExchangeList'; -import {PrivateKey, PublicKey} from 'steem/lib/auth/ecc'; +import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc'; export function validate_account_name(value, memo) { let i, label, len, length, ref, suffix; diff --git a/src/app/utils/ConsoleExports.js b/src/app/utils/ConsoleExports.js index 1e3ce8987..0d3387b07 100644 --- a/src/app/utils/ConsoleExports.js +++ b/src/app/utils/ConsoleExports.js @@ -1,4 +1,4 @@ -import {PrivateKey, PublicKey, Aes, key_utils} from 'steem/lib/auth/ecc'; +import {PrivateKey, PublicKey, Aes, key_utils} from '@steemit/steem-js/lib/auth/ecc'; // import secureRandom from 'secure-random' diff --git a/src/app/utils/ServerApiClient.js b/src/app/utils/ServerApiClient.js index d4bae6d75..eb757346e 100644 --- a/src/app/utils/ServerApiClient.js +++ b/src/app/utils/ServerApiClient.js @@ -1,5 +1,5 @@ import {NTYPES, notificationsArrayToMap} from 'app/utils/Notifications'; -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; const request_base = { method: 'post', diff --git a/src/server/api/account_recovery.js b/src/server/api/account_recovery.js index cc94eca5d..d2542f1fe 100644 --- a/src/server/api/account_recovery.js +++ b/src/server/api/account_recovery.js @@ -4,7 +4,7 @@ import models from 'db/models'; import config from 'config'; import {esc, escAttrs} from 'db/models'; import {getRemoteIp, rateLimitReq, checkCSRF} from 'server/utils/misc'; -import {broadcast} from 'steem'; +import {broadcast} from '@steemit/steem-js'; export default function useAccountRecoveryApi(app) { const router = koa_router(); diff --git a/src/server/api/general.js b/src/server/api/general.js index b14361a5f..4800ecbd8 100644 --- a/src/server/api/general.js +++ b/src/server/api/general.js @@ -10,8 +10,8 @@ import {emailRegex, getRemoteIp, rateLimitReq, checkCSRF} from 'server/utils/mis import coBody from 'co-body'; import Mixpanel from 'mixpanel'; import Tarantool from 'db/tarantool'; -import {PublicKey, Signature, hash} from 'steem/lib/auth/ecc'; -import {api, broadcast} from 'steem'; +import {PublicKey, Signature, hash} from '@steemit/steem-js/lib/auth/ecc'; +import {api, broadcast} from '@steemit/steem-js'; const mixpanel = config.get('mixpanel') ? Mixpanel.init(config.get('mixpanel')) : null; diff --git a/src/server/index.js b/src/server/index.js index b619f3d56..dbfc342cf 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -1,6 +1,6 @@ import config from 'config'; -import * as steem from 'steem'; +import * as steem from '@steemit/steem-js'; const path = require('path'); const ROOT = path.join(__dirname, '../..'); diff --git a/src/server/json/post_json.jsx b/src/server/json/post_json.jsx index c9cc82e5e..e95a00edf 100644 --- a/src/server/json/post_json.jsx +++ b/src/server/json/post_json.jsx @@ -1,7 +1,7 @@ import koa_router from 'koa-router'; import React from 'react'; import {routeRegex} from "app/ResolveRoute"; -import {api} from 'steem' +import {api} from '@steemit/steem-js' export default function usePostJson(app) { const router = koa_router(); diff --git a/src/server/json/user_json.jsx b/src/server/json/user_json.jsx index a296e535b..0be4631c2 100644 --- a/src/server/json/user_json.jsx +++ b/src/server/json/user_json.jsx @@ -1,7 +1,7 @@ import koa_router from 'koa-router'; import React from 'react'; import {routeRegex} from "app/ResolveRoute"; -import {api} from 'steem' +import {api} from '@steemit/steem-js' export default function useUserJson(app) { const router = koa_router(); diff --git a/src/server/sign_up_pages/enter_confirm_email.jsx b/src/server/sign_up_pages/enter_confirm_email.jsx index ffe2b623a..c8dab793a 100644 --- a/src/server/sign_up_pages/enter_confirm_email.jsx +++ b/src/server/sign_up_pages/enter_confirm_email.jsx @@ -12,7 +12,7 @@ import MiniHeader from "app/components/modules/MiniHeader"; import secureRandom from "secure-random"; import Mixpanel from "mixpanel"; import Progress from "react-foundation-components/lib/global/progress-bar"; -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; const path = require('path'); const ROOT = path.join(__dirname, '../../..'); diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index a8c25025b..b70ac3c85 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -28,7 +28,7 @@ import {notificationsArrayToMap} from 'app/utils/Notifications'; import {routeRegex} from "app/ResolveRoute"; import {contentStats} from 'app/utils/StateFunctions' -import {api} from 'steem'; +import {api} from '@steemit/steem-js'; const sagaMiddleware = createSagaMiddleware( ...userWatches, // keep first to remove keys early when a page change happens diff --git a/src/shared/api_client/ChainConfig.js b/src/shared/api_client/ChainConfig.js index 52a975d5c..895d9ee89 100644 --- a/src/shared/api_client/ChainConfig.js +++ b/src/shared/api_client/ChainConfig.js @@ -1,4 +1,4 @@ -import * as steem from 'steem' +import * as steem from '@steemit/steem-js' steem.config.set('address_prefix', 'STM') diff --git a/yarn.lock b/yarn.lock index 047b023a3..c50f1012c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,28 @@ libsodium "^0.4.8" libsodium-wrappers "^0.4.8" +"@steemit/steem-js@^0.6.5": + version "0.6.5" + resolved "https://registry.yarnpkg.com/@steemit/steem-js/-/steem-js-0.6.5.tgz#a571eed61c71dd9bbae411da195732c9aaf34f3b" + dependencies: + bigi "^1.4.2" + bluebird "^3.4.6" + browserify-aes "^1.0.6" + bs58 "^4.0.0" + buffer "^5.0.6" + bytebuffer "^5.0.1" + create-hash "^1.1.2" + create-hmac "^1.1.4" + cross-env "^5.0.0" + debug "^2.6.8" + detect-node "^2.0.3" + ecurve "^1.0.5" + fetch-ponyfill "^4.1.0" + lodash "^4.16.4" + postinstall-build "^5.0.1" + secure-random "^1.1.1" + ws "^3.0.0" + "@types/geojson@^1.0.0": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.5.tgz#ef9f12277233399c7f32086818a56a84c8952f8f" @@ -7485,28 +7507,6 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -"steem@github:steemit/steem-js#72a36c835a74f446da47e36feea5d5f1f4dde55b": - version "0.6.5" - resolved "https://codeload.github.com/steemit/steem-js/tar.gz/72a36c835a74f446da47e36feea5d5f1f4dde55b" - dependencies: - bigi "^1.4.2" - bluebird "^3.4.6" - browserify-aes "^1.0.6" - bs58 "^4.0.0" - buffer "^5.0.6" - bytebuffer "^5.0.1" - create-hash "^1.1.2" - create-hmac "^1.1.4" - cross-env "^5.0.0" - debug "^2.6.8" - detect-node "^2.0.3" - ecurve "^1.0.5" - fetch-ponyfill "^4.1.0" - lodash "^4.16.4" - postinstall-build "^5.0.1" - secure-random "^1.1.1" - ws "^3.0.0" - store@^1.3.20: version "1.3.20" resolved "https://registry.yarnpkg.com/store/-/store-1.3.20.tgz#13ea7e3fb2d6c239868265d686b1d84e99c5be3e" -- GitLab From 3e26f9a322e965d4c273db0ed15c73c46f37a779 Mon Sep 17 00:00:00 2001 From: Leo Ruther Valles Date: Wed, 15 Nov 2017 12:24:20 +0800 Subject: [PATCH 029/399] Fixed Night Mode Buttons Hover and Focus States --- src/app/assets/stylesheets/_themes.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/app/assets/stylesheets/_themes.scss b/src/app/assets/stylesheets/_themes.scss index f0da826f6..4feb25dcf 100755 --- a/src/app/assets/stylesheets/_themes.scss +++ b/src/app/assets/stylesheets/_themes.scss @@ -315,3 +315,14 @@ $themes: ( } } } + + +.theme-dark { + .button.hollow { + &:hover, &:focus { + border-color: $color-teal; + color: $color-teal; + outline-color: $color-teal; + } + } +} \ No newline at end of file -- GitLab From e21e040ba6fa731052110d4054589406f228d18e Mon Sep 17 00:00:00 2001 From: Originate Date: Tue, 14 Nov 2017 22:49:24 -0800 Subject: [PATCH 030/399] Improve user confusion on creating new post, hide duplicate post button only on submit page --- src/app/components/modules/TopRightMenu.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/app/components/modules/TopRightMenu.jsx b/src/app/components/modules/TopRightMenu.jsx index 96e125c08..933fb41db 100644 --- a/src/app/components/modules/TopRightMenu.jsx +++ b/src/app/components/modules/TopRightMenu.jsx @@ -21,7 +21,7 @@ const defaultNavigate = (e) => { browserHistory.push(a.pathname + a.search + a.hash); }; -function TopRightMenu({username, showLogin, logout, loggedIn, vertical, navigate, toggleOffCanvasMenu, probablyLoggedIn, nightmodeEnabled, toggleNightmode}) { +function TopRightMenu({username, showLogin, logout, loggedIn, vertical, navigate, toggleOffCanvasMenu, probablyLoggedIn, nightmodeEnabled, toggleNightmode, userPath}) { const mcn = 'menu' + (vertical ? ' vertical show-for-small-only' : ''); const mcl = vertical ? '' : ' sub-menu'; const lcn = vertical ? '' : 'show-for-medium'; @@ -36,6 +36,7 @@ function TopRightMenu({username, showLogin, logout, loggedIn, vertical, navigate const reset_password_link = `/@${username}/password`; const settings_link = `/@${username}/settings`; const tt_search = tt('g.search'); + const pathCheck = userPath === '/submit.html' ? true : null; if (loggedIn) { // change back to if(username) after bug fix: Clicking on Login does not cause drop-down to close #TEMP! const user_menu = [ {link: feed_link, icon: "home", value: tt('g.feed'), addon: }, @@ -53,7 +54,7 @@ function TopRightMenu({username, showLogin, logout, loggedIn, vertical, navigate return (
    • {vertical ? {tt_search} : }
    • - {submit_story} + {!pathCheck ? submit_story : null} {!vertical && submit_icon} Date: Thu, 16 Nov 2017 01:53:16 +0100 Subject: [PATCH 031/399] Peg steem-js version --- package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0b26a9373..e8dfe494f 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "sequelize-cli": "^2.3.1", "speakingurl": "^9.0.0", "sqlite3": "^3.1.8", - "@steemit/steem-js": "^0.6.5", + "@steemit/steem-js": "0.6.5", "store": "^1.3.20", "style-loader": "^0.18.2", "svg-inline-loader": "^0.8.0", diff --git a/yarn.lock b/yarn.lock index c50f1012c..81fee0129 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,7 +10,7 @@ libsodium "^0.4.8" libsodium-wrappers "^0.4.8" -"@steemit/steem-js@^0.6.5": +"@steemit/steem-js@0.6.5": version "0.6.5" resolved "https://registry.yarnpkg.com/@steemit/steem-js/-/steem-js-0.6.5.tgz#a571eed61c71dd9bbae411da195732c9aaf34f3b" dependencies: -- GitLab From b7e74e35b3714e8268fa11ac9389e607ef59cf46 Mon Sep 17 00:00:00 2001 From: "Andrew Chaney (netuoso)" Date: Wed, 15 Nov 2017 23:05:40 -0600 Subject: [PATCH 032/399] Add target blank to User Profile website link to prevent the window location from changing. --- src/app/components/pages/UserProfile.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/pages/UserProfile.jsx b/src/app/components/pages/UserProfile.jsx index 9774ede7a..89c639dfc 100644 --- a/src/app/components/pages/UserProfile.jsx +++ b/src/app/components/pages/UserProfile.jsx @@ -474,7 +474,7 @@ export default class UserProfile extends React.Component {

    {location && {location}} - {website && {website_label}} + {website && {website_label}}

    -- GitLab From 03c524fadbfed325cfd9936f45e6e52c43c3e6af Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Thu, 16 Nov 2017 18:29:35 -0600 Subject: [PATCH 033/399] 2013 - detect if user has scrolled in an up direction. Reduce scroll attempt rate. --- src/shared/UniversalRender.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index b6bf72380..43e25bc8d 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -66,6 +66,9 @@ const scrollTop = (el, topOffset, prevDocumentInfo, triesRemaining) => { scrollTarget: calcOffsetRoot(el) + topOffset }; + if(prevDocumentInfo.scrollTop > documentInfo.scrollTop) { //detecting that the user has scrolled in an up direction + return; + } if(documentInfo.scrollTop < documentInfo.scrollTarget || prevDocumentInfo.scrollTarget < documentInfo.scrollTarget || prevDocumentInfo.scrollHeight < documentInfo.scrollHeight) { -- GitLab From 34415e7c40ef52ccd3ec4c3679837f79716bc330 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Thu, 16 Nov 2017 18:37:00 -0600 Subject: [PATCH 034/399] 2013 descriptive constant --- src/shared/UniversalRender.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index 43e25bc8d..7df827c5b 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -42,9 +42,10 @@ const calcOffsetRoot = (startEl) => { }; //BEGIN: SCROLL CODE -const SCROLL_TOP_TRIES = 100; -const SCROLL_TOP_DELAY_MS = 50; +const SCROLL_TOP_TRIES = 50; +const SCROLL_TOP_DELAY_MS = 100; const SCROLL_TOP_EXTRA_PIXEL_OFFSET = 3; +const SCROLL_UP_FUDGE_PIXELS = 10; let scrollTopTimeout = null; @@ -66,7 +67,7 @@ const scrollTop = (el, topOffset, prevDocumentInfo, triesRemaining) => { scrollTarget: calcOffsetRoot(el) + topOffset }; - if(prevDocumentInfo.scrollTop > documentInfo.scrollTop) { //detecting that the user has scrolled in an up direction + if(prevDocumentInfo.scrollTop > (documentInfo.scrollTop + SCROLL_UP_FUDGE_PIXELS)) { //detecting that the user has scrolled in an up direction return; } if(documentInfo.scrollTop < documentInfo.scrollTarget -- GitLab From 4dc27d6564edd7bcbeea34e7fcf6d37eb93389f5 Mon Sep 17 00:00:00 2001 From: drakos <4613678+Jolly-Pirate@users.noreply.github.com> Date: Thu, 16 Nov 2017 23:04:09 -0500 Subject: [PATCH 035/399] yarn --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 047b023a3..5c7d749ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7485,9 +7485,9 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -"steem@github:steemit/steem-js#72a36c835a74f446da47e36feea5d5f1f4dde55b": - version "0.6.5" - resolved "https://codeload.github.com/steemit/steem-js/tar.gz/72a36c835a74f446da47e36feea5d5f1f4dde55b" +"steem@github:steemit/steem-js#ec4439c1319e21ccb4b52a3de3798a37b84b0001": + version "0.6.4" + resolved "https://codeload.github.com/steemit/steem-js/tar.gz/ec4439c1319e21ccb4b52a3de3798a37b84b0001" dependencies: bigi "^1.4.2" bluebird "^3.4.6" -- GitLab From 7b325abecd513137d116ab85ee604b09ad55346b Mon Sep 17 00:00:00 2001 From: Originate Date: Thu, 16 Nov 2017 22:32:34 -0800 Subject: [PATCH 036/399] Add additional bad actor impersonating exchange --- src/app/utils/BadActorList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index 62d9977ee..c15dd4ff4 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -26,6 +26,7 @@ btrex bttrex ittrex bittrex-deposit +openledgerdex poloiex poloinex polomiex -- GitLab From 17a2664d08bff4c6f9aa26c52780f69623ed5e50 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 17 Nov 2017 14:51:48 +0100 Subject: [PATCH 037/399] reverse tabnabbing precaution --- src/app/components/pages/UserProfile.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/pages/UserProfile.jsx b/src/app/components/pages/UserProfile.jsx index 89c639dfc..f6fe1bf28 100644 --- a/src/app/components/pages/UserProfile.jsx +++ b/src/app/components/pages/UserProfile.jsx @@ -474,7 +474,7 @@ export default class UserProfile extends React.Component {

    {location && {location}} - {website && {website_label}} + {website && {website_label}}

    -- GitLab From 48c50d0828acf31f79d0944b4fe84d82bf32d58e Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Fri, 17 Nov 2017 14:52:46 -0600 Subject: [PATCH 038/399] Re-evaluation of code suggests a different pattern will be used --- src/app/utils/UserUtil.js | 66 +++------------------------------------ 1 file changed, 4 insertions(+), 62 deletions(-) diff --git a/src/app/utils/UserUtil.js b/src/app/utils/UserUtil.js index b8eb39aa0..68cc9b70f 100644 --- a/src/app/utils/UserUtil.js +++ b/src/app/utils/UserUtil.js @@ -1,67 +1,9 @@ -/** - * This file replicates and extends the contents of User.js in branches new-url-scheme && notifications-ui. - * Additions here need to be added to ./User.js when those are merged. Imports should pull that file, and this one should be deleted. - */ - - -let store = false; -const MSG_STORE_NOT_SET = 'store is not set'; - -export const setStore = (theStore) => { - if(!store) { - store = theStore; - } else { - throw Error("Routes.setStore - store has already been set."); - } -} - -/** - * returns the current user's username *if* there is one. - * - * later can be modified to return full user object if passed *true* for 1st argument - * @returns {boolean} - */ -export const currentUser = () => { - if (store) { - const state = store.getState(); - if (state.user) { - const current = state.user.getIn(['current']); - if (current) { - return current; - } - } - } else { - console.warn(MSG_STORE_NOT_SET); - } - return false; -} -/** - * returns the current user's username *if* there is one. - * - * later can be modified to return full user object if passed *true* for 1st argument - * @returns {boolean} - */ -export const currentUsername = () => { - if (store) { - const state = store.getState(); - if (state.user) { - const uName = state.user.getIn(['current', 'username']); - if (uName) { - return uName; - } - } - } else { - console.warn(MSG_STORE_NOT_SET); - } - return false; -} - -export const isOwnAccount = (username) => { - return (username === currentUsername()); -} - /** * * @returns {boolean} */ export const isLoggedIn = () => typeof localStorage !== 'undefined' && !!localStorage.getItem('autopost2'); + +export default { + isLoggedIn +}; -- GitLab From 96731b442a8ac65b40be39214f763f7ff5766356 Mon Sep 17 00:00:00 2001 From: drakos <4613678+Jolly-Pirate@users.noreply.github.com> Date: Fri, 17 Nov 2017 17:31:32 -0500 Subject: [PATCH 039/399] update yarn from master --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a8127f0e4..5d26d1548 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7485,9 +7485,9 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -"steem@github:steemit/steem-js#ec4439c1319e21ccb4b52a3de3798a37b84b0001": - version "0.6.4" - resolved "https://codeload.github.com/steemit/steem-js/tar.gz/ec4439c1319e21ccb4b52a3de3798a37b84b0001" +"steem@github:steemit/steem-js#72a36c835a74f446da47e36feea5d5f1f4dde55b": + version "0.6.5" + resolved "https://codeload.github.com/steemit/steem-js/tar.gz/72a36c835a74f446da47e36feea5d5f1f4dde55b" dependencies: bigi "^1.4.2" bluebird "^3.4.6" -- GitLab From 4788390a22afaaea19e537628d77a229ab020222 Mon Sep 17 00:00:00 2001 From: "Andrew Chaney (netuoso)" Date: Fri, 17 Nov 2017 22:52:28 -0600 Subject: [PATCH 040/399] Make the author reward links in the Wallet history tab clickable. --- src/app/components/cards/TransferHistoryRow.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/components/cards/TransferHistoryRow.jsx b/src/app/components/cards/TransferHistoryRow.jsx index ea1db55ba..7941e56f3 100644 --- a/src/app/components/cards/TransferHistoryRow.jsx +++ b/src/app/components/cards/TransferHistoryRow.jsx @@ -72,8 +72,8 @@ class TransferHistoryRow extends React.Component { } else if (type === 'author_reward') { let steem_payout = ""; if(data.steem_payout !== '0.000 STEEM') steem_payout = ", " + data.steem_payout; - description_start += `${data.sbd_payout}${steem_payout}, ${tt('g.and')} ${author_reward} STEEM POWER ${tt('g.for')} ${data.author}/${data.permlink}`; - // other_account = ``; + description_start += `${data.sbd_payout}${steem_payout}, ${tt('g.and')} ${author_reward} STEEM POWER ${tt('g.for')}`; + other_account = data.author + "/" + data.permlink; description_end = ''; } else if (type === 'claim_reward_balance') { -- GitLab From b01042ce8f78ac458dda3a1bffe68657d4635c12 Mon Sep 17 00:00:00 2001 From: Originate Date: Fri, 17 Nov 2017 22:56:19 -0800 Subject: [PATCH 041/399] Correct issue with naming on meta tags not rendering properly --- src/app/utils/ExtractMeta.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/utils/ExtractMeta.js b/src/app/utils/ExtractMeta.js index e2745db51..ac0f24108 100644 --- a/src/app/utils/ExtractMeta.js +++ b/src/app/utils/ExtractMeta.js @@ -41,15 +41,15 @@ export default function extractMeta(chain_data, rp) { metas.push({name: 'description', content: desc}); // Open Graph data - metas.push({property: 'og:title', content: title}); - metas.push({property: 'og:type', content: 'article'}); - metas.push({property: 'og:url', content: url}); - metas.push({property: 'og:image', content: image || 'https://steemit.com/images/steemit.png'}); - metas.push({property: 'og:description', content: desc}); - metas.push({property: 'og:site_name', content: 'Steemit'}); - metas.push({property: 'fb:app_id', content: $STM_Config.fb_app}); - metas.push({property: 'article:tag', content: category}); - metas.push({property: 'article:published_time', content: created}); + metas.push({name: 'og:title', content: title}); + metas.push({name: 'og:type', content: 'article'}); + metas.push({name: 'og:url', content: url}); + metas.push({name: 'og:image', content: image || 'https://steemit.com/images/steemit.png'}); + metas.push({name: 'og:description', content: desc}); + metas.push({name: 'og:site_name', content: 'Steemit'}); + metas.push({name: 'fb:app_id', content: $STM_Config.fb_app}); + metas.push({name: 'article:tag', content: category}); + metas.push({name: 'article:published_time', content: created}); // Twitter card data metas.push({name: 'twitter:card', content: image ? 'summary_large_image' : 'summary'}); -- GitLab From bc61a722302c0148b14797ba3142654e281dbbef Mon Sep 17 00:00:00 2001 From: Originate Date: Fri, 17 Nov 2017 23:34:06 -0800 Subject: [PATCH 042/399] Adjust border to prevent wrapping in footer of full post --- src/app/components/cards/PostFull.scss | 59 +++++++++++++------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/app/components/cards/PostFull.scss b/src/app/components/cards/PostFull.scss index 0cfad708b..ac10152ea 100644 --- a/src/app/components/cards/PostFull.scss +++ b/src/app/components/cards/PostFull.scss @@ -1,7 +1,7 @@ .Post { @include themify($themes) { - background-color: themed('moduleBackgroundColor'); - } + background-color: themed('moduleBackgroundColor'); + } } .PostFull { @@ -10,13 +10,13 @@ margin: 0 auto; max-width: 50rem; @include themify($themes) { - border-bottom: themed('border'); - } + border-bottom: themed('border'); + } .button.hollow { @include themify($themes) { - border: themed('borderAccent'); - color: themed('textColorAccent'); - } + border: themed('borderAccent'); + color: themed('textColorAccent'); + } } .ReplyEditor { @@ -26,8 +26,9 @@ .PostFull__time_author_category { font-weight: 400; + border-right: none!important; @include themify($themes) { - border-right: themed('border'); + border-right: themed('border'); color: themed('textColorSecondary'); } @@ -42,12 +43,12 @@ margin: 1rem 0 1rem 0; line-height: 1.2; @include themify($themes) { - color: themed('textColorSecondary'); - } + color: themed('textColorSecondary'); + } strong, a { @include themify($themes) { - color: themed('textColorSecondary'); - } + color: themed('textColorSecondary'); + } } font-size: 120%; display: flex; @@ -70,8 +71,8 @@ .PostFull__header { @include themify($themes) { - border-bottom: themed('border'); - } + border-bottom: themed('border'); + } > h1 { overflow: hidden; font-family: $body-font-family; @@ -95,8 +96,8 @@ top: 5px; svg { @include themify($themes) { - fill: themed('textColorSecondary'); - } + fill: themed('textColorSecondary'); + } } } } @@ -117,8 +118,8 @@ font-size: 94%; svg { @include themify($themes) { - fill: themed('textColorSecondary'); - } + fill: themed('textColorSecondary'); + } } a, .FoundationDropdownMenu__label { font-size: 94%; @@ -132,8 +133,8 @@ } .VerticalMenu > li > a { @include themify($themes) { - fill: themed('textColorSecondary'); - } + fill: themed('textColorSecondary'); + } } span { white-space: normal; @@ -143,8 +144,8 @@ padding-right: .4rem; margin-right: .4rem; @include themify($themes) { - border-right: themed('border'); - } + border-right: themed('border'); + } } } @@ -158,24 +159,24 @@ font-size: 94%; font-weight: 600; @include themify($themes) { - border-right: themed('border'); - fill: themed('textColorSecondary'); - } + border-right: themed('border'); + fill: themed('textColorSecondary'); + } } .PostFull__reply { padding-right: .4rem; margin-right: .4rem; @include themify($themes) { - border-right: themed('border'); - } + border-right: themed('border'); + } a {margin: 0 0.15rem;} } .PostFull__lifetime { @include themify($themes) { - fill: themed('textColorSecondary'); - } + fill: themed('textColorSecondary'); + } font-size: 80%; } -- GitLab From 574480af930cdb1a554eea954c9f4cce32195a93 Mon Sep 17 00:00:00 2001 From: "Andrew Chaney (netuoso)" Date: Sun, 19 Nov 2017 13:33:55 -0600 Subject: [PATCH 043/399] Fix the syntax for the internationalization of the error encountered when a user inputs too much in the reply editor. --- src/app/components/elements/ReplyEditor.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/components/elements/ReplyEditor.jsx b/src/app/components/elements/ReplyEditor.jsx index 370bfbd66..693e09840 100644 --- a/src/app/components/elements/ReplyEditor.jsx +++ b/src/app/components/elements/ReplyEditor.jsx @@ -143,7 +143,7 @@ class ReplyEditor extends React.Component { initForm(props) { const {isStory, type, fields} = props const isEdit = type === 'edit' - const maxKb = isStory ? 100 : 16 + const maxKb = isStory ? 65 : 16 reactForm({ fields, instance: this, @@ -157,7 +157,7 @@ class ReplyEditor extends React.Component { ), category: isStory && validateCategory(values.category, !isEdit), body: !values.body ? tt('g.required') : - values.body.length > maxKb * 1024 ? tt('reply_editor.exceeds_maximum_length', maxKb) : + values.body.length > maxKb * 1024 ? tt('reply_editor.exceeds_maximum_length', {maxKb}) : null }) }) -- GitLab From ddfe1aa512573d68b2069ccced034fb894cfc738 Mon Sep 17 00:00:00 2001 From: sirrius-a-b Date: Sun, 19 Nov 2017 20:47:02 +0100 Subject: [PATCH 044/399] Fix for downvoted comment avatar overflow --- src/app/components/cards/Comment.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/cards/Comment.jsx b/src/app/components/cards/Comment.jsx index 873bc7bf2..b07c7bd90 100644 --- a/src/app/components/cards/Comment.jsx +++ b/src/app/components/cards/Comment.jsx @@ -307,7 +307,7 @@ class CommentImpl extends React.Component { commentClasses.push(this.props.root ? 'root' : 'reply') if(hide_body || this.state.collapsed) commentClasses.push('collapsed'); - let innerCommentClass = ignore || gray ? 'downvoted' : '' + let innerCommentClass = ignore || gray ? 'downvoted clearfix' : '' if(this.state.highlight) innerCommentClass += ' highlighted' //console.log(comment); -- GitLab From e1ce8c26185e5c1f648d01fb1dc8b030b5a08036 Mon Sep 17 00:00:00 2001 From: "Andrew Chaney (netuoso)" Date: Sun, 19 Nov 2017 14:06:21 -0600 Subject: [PATCH 045/399] Add notranslate class to image upload helper to prevent google from intercepting the link --- src/app/components/elements/ReplyEditor.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/elements/ReplyEditor.jsx b/src/app/components/elements/ReplyEditor.jsx index 370bfbd66..2761a96f3 100644 --- a/src/app/components/elements/ReplyEditor.jsx +++ b/src/app/components/elements/ReplyEditor.jsx @@ -390,7 +390,7 @@ class ReplyEditor extends React.Component { tabIndex={2} /> {type === 'submit_story' && -

    +

    {tt('reply_editor.insert_images_by_dragging_dropping')} {noClipboardData ? '' : tt('reply_editor.pasting_from_the_clipboard')} {tt('reply_editor.or_by')} {tt('reply_editor.selecting_them')}. -- GitLab From b95fa27e48955514fe28cffc675e164b7ee879e6 Mon Sep 17 00:00:00 2001 From: "Andrew Chaney (netuoso)" Date: Sun, 19 Nov 2017 15:26:27 -0600 Subject: [PATCH 046/399] Add a better color for the price warning color on the currency market page for the dark theme. --- src/app/assets/stylesheets/_themes.scss | 1 + src/app/components/pages/Market.scss | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/app/assets/stylesheets/_themes.scss b/src/app/assets/stylesheets/_themes.scss index f0da826f6..ca28461e7 100755 --- a/src/app/assets/stylesheets/_themes.scss +++ b/src/app/assets/stylesheets/_themes.scss @@ -99,6 +99,7 @@ $themes: ( buttonTextHover: $color-white, buttonBoxShadow: $color-teal, buttonBoxShadowHover: $color-white, + inputPriceWarning: rgba(255, 153, 0, 0.83), ), ); diff --git a/src/app/components/pages/Market.scss b/src/app/components/pages/Market.scss index 6e57e5ddf..4967ea193 100644 --- a/src/app/components/pages/Market.scss +++ b/src/app/components/pages/Market.scss @@ -77,6 +77,9 @@ input.sell-color:hover { input.price_warning { background: rgba(255, 153, 0, 0.13); + @include themify($themes) { + background: themed('inputPriceWarning'); + } } } -- GitLab From dc87a67a2e9a24ad6813cd906a5fa9cf8a83d505 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Mon, 20 Nov 2017 20:50:03 +0100 Subject: [PATCH 047/399] yet another one --- src/app/utils/BadActorList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index 2204ab832..5aba0c262 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -84,6 +84,7 @@ blocktrade blocktradees blocktraded blocktrader +blocktrads bocktrades changelly.com changely -- GitLab From be644a7a87157fdba1806b2be0d13b404a013171 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Mon, 20 Nov 2017 19:00:30 -0800 Subject: [PATCH 048/399] Clarified Steem log is the blue logo --- src/app/help/en/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index 21e811a17..3d71ad27d 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1223,7 +1223,7 @@ The Steemit brand and logo are protected by intellectual property laws, includin Steemit also considers posts which are created on the Steem blockchain and earn rewards through the blockchain rewards mechanism an authorized form of commercial use. -The Steem logo is licensed under Creative Commons CC0, meaning you’re free to copy, modify, or distribute (even for commercial purposes) without needing to ask permission or give attribution. +The blue Steem logo is licensed under Creative Commons CC0, meaning you’re free to copy, modify, or distribute (even for commercial purposes) without needing to ask permission or give attribution. ^ -- GitLab From ba97ac108e834369b1e73c58cc62a5731e752d01 Mon Sep 17 00:00:00 2001 From: Originate Date: Tue, 21 Nov 2017 01:48:07 -0800 Subject: [PATCH 049/399] Change default behavior message for transfers improved asking for password --- src/app/components/modules/LoginForm.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/components/modules/LoginForm.jsx b/src/app/components/modules/LoginForm.jsx index 2a7d49944..bff66efd7 100644 --- a/src/app/components/modules/LoginForm.jsx +++ b/src/app/components/modules/LoginForm.jsx @@ -140,7 +140,7 @@ class LoginForm extends Component { postType = 'Login to Follow Users' } else if (loginBroadcastOperation) { // check for post or comment in operation - postType = loginBroadcastOperation.getIn(['operation', 'title']) ? tt('loginform_jsx.login_to_post') : tt('loginform_jsx.login_to_comment'); + postType = loginBroadcastOperation.getIn(['operation', 'title']) ? tt('loginform_jsx.login_to_post') : tt('g.confirm_password'); } const title = postType ? postType : tt('g.login'); const authType = /^vote|comment/.test(opType) ? tt('loginform_jsx.posting') : tt('loginform_jsx.active_or_owner'); @@ -199,7 +199,7 @@ class LoginForm extends Component { }

    -- GitLab From 8e5622460999fa7ad8d448811d692af26edd3774 Mon Sep 17 00:00:00 2001 From: Originate Date: Tue, 21 Nov 2017 01:50:06 -0800 Subject: [PATCH 050/399] Terminate properly eslint style prior code --- src/app/components/modules/LoginForm.jsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/components/modules/LoginForm.jsx b/src/app/components/modules/LoginForm.jsx index bff66efd7..e85b098e7 100644 --- a/src/app/components/modules/LoginForm.jsx +++ b/src/app/components/modules/LoginForm.jsx @@ -46,8 +46,8 @@ class LoginForm extends Component { if (onCancel) onCancel() }; this.qrReader = () => { - const {qrReader} = props - const {password} = this.state + const {qrReader} = props; + const {password} = this.state; qrReader(data => {password.props.onChange(data)}) }; this.initForm(props) @@ -248,7 +248,7 @@ function urlAccountName() { function checkPasswordChecksum(password) { // A Steemit generated password is a WIF prefixed with a P .. // It is possible to login directly with a WIF - const wif = /^P/.test(password) ? password.substring(1) : password + const wif = /^P/.test(password) ? password.substring(1) : password; if(!/^5[HJK].{45,}/i.test(wif)) {// 51 is the wif length // not even close @@ -263,19 +263,19 @@ export default connect( // mapStateToProps (state) => { - const login_error = state.user.get('login_error') - const currentUser = state.user.get('current') - const loginBroadcastOperation = state.user.get('loginBroadcastOperation') + const login_error = state.user.get('login_error'); + const currentUser = state.user.get('current'); + const loginBroadcastOperation = state.user.get('loginBroadcastOperation'); const initialValues = { saveLogin: saveLoginDefault, } // The username input has a value prop, so it should not use initialValues - const initialUsername = currentUser && currentUser.has('username') ? currentUser.get('username') : urlAccountName() - const loginDefault = state.user.get('loginDefault') + const initialUsername = currentUser && currentUser.has('username') ? currentUser.get('username') : urlAccountName(); + const loginDefault = state.user.get('loginDefault'); if(loginDefault) { - const {username, authType} = loginDefault.toJS() + const {username, authType} = loginDefault.toJS(); if(username && authType) initialValues.username = username + '/' + authType } else if (initialUsername) { initialValues.username = initialUsername; -- GitLab From c120086cd0b45816b8d12930bdfed893395ec72f Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 11:37:00 -0600 Subject: [PATCH 051/399] #1989 change `?whistle_signup` to `?view_mode=whistle` --- src/app/Main.js | 9 ++++++--- src/app/components/App.jsx | 9 +++++---- src/shared/constants.js | 7 +++++++ 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 src/shared/constants.js diff --git a/src/app/Main.js b/src/app/Main.js index 7f312f383..f262dcb7b 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -1,6 +1,7 @@ import 'babel-core/register'; import 'babel-polyfill'; import 'whatwg-fetch'; +import {VIEW_MODE_WHISTLE, PARAM_VIEW_MODE} from 'shared/constants'; import './assets/stylesheets/app.scss'; import plugins from 'app/utils/JsPlugins'; import Iso from 'iso'; @@ -9,13 +10,14 @@ import ConsoleExports from './utils/ConsoleExports'; import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; import * as steem from 'steem'; + window.onerror = error => { if (window.$STM_csrf) serverApiRecordEvent('client_error', error); }; -const CMD_LOG_T = 'log-t' -const CMD_LOG_TOGGLE = 'log-toggle' -const CMD_LOG_O = 'log-on' +const CMD_LOG_T = 'log-t'; +const CMD_LOG_TOGGLE = 'log-toggle'; +const CMD_LOG_O = 'log-on'; try { if(process.env.NODE_ENV === 'development') { @@ -87,6 +89,7 @@ function runApp(initial_state) { window.$STM_csrf = initial_state.offchain.csrf; delete initial_state.offchain.csrf; } + initial_state.app.viewMode = (window.location.search.indexOf(PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE))? VIEW_MODE_WHISTLE : ''; const location = `${window.location.pathname}${window.location.search}${window.location.hash}`; universalRender({history, location, initial_state}) diff --git a/src/app/components/App.jsx b/src/app/components/App.jsx index 9e62145c1..ceb90f72d 100644 --- a/src/app/components/App.jsx +++ b/src/app/components/App.jsx @@ -20,6 +20,7 @@ import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; import { APP_NAME, VESTING_TOKEN, LIQUID_TOKEN } from 'app/client_config'; import {key_utils} from 'steem/lib/auth/ecc'; import resolveRoute from 'app/ResolveRoute'; +import {VIEW_MODE_WHISTLE} from 'shared/constants'; const pageRequiresEntropy = (path) => { const {page} = resolveRoute(path); @@ -125,10 +126,11 @@ class App extends React.Component { render() { const {location, params, children, flash, new_visitor, - depositSteem, signup_bonus, username, nightmodeEnabled} = this.props; + depositSteem, signup_bonus, username, nightmodeEnabled, viewMode} = this.props; const lp = false; //location.pathname === '/'; const miniHeader = location.pathname === '/create_account' || location.pathname === '/pick_account'; const headerHidden = miniHeader && location.search === '?whistle_signup' + const whistleView = (viewMode === VIEW_MODE_WHISTLE); const params_keys = Object.keys(params); const ip = location.pathname === '/' || (params_keys.length === 2 && params_keys[0] === 'order' && params_keys[1] === 'category'); const alert = this.props.error || flash.get('alert') || flash.get('error'); @@ -193,9 +195,7 @@ class App extends React.Component { const themeClass = nightmodeEnabled ? ' theme-dark' : ' theme-light'; - return
    + return
      @@ -317,6 +317,7 @@ App.propTypes = { export default connect( state => { return { + viewMode: state.app.get('viewMode'), error: state.app.get('error'), flash: state.offchain.get('flash'), signup_bonus: state.offchain.get('signup_bonus'), diff --git a/src/shared/constants.js b/src/shared/constants.js new file mode 100644 index 000000000..d8a8565e2 --- /dev/null +++ b/src/shared/constants.js @@ -0,0 +1,7 @@ +export const VIEW_MODE_WHISTLE = 'whistle'; +export const PARAM_VIEW_MODE = 'view_mode'; + +export default { + VIEW_MODE_WHISTLE, + PARAM_VIEW_MODE +} -- GitLab From 60155c4c2b57304d034123df79dac5e822008ca0 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 11:42:41 -0600 Subject: [PATCH 052/399] #1989 actually check for presence. --- src/app/Main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Main.js b/src/app/Main.js index f262dcb7b..f9f284f69 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -89,7 +89,7 @@ function runApp(initial_state) { window.$STM_csrf = initial_state.offchain.csrf; delete initial_state.offchain.csrf; } - initial_state.app.viewMode = (window.location.search.indexOf(PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE))? VIEW_MODE_WHISTLE : ''; + initial_state.app.viewMode = (window.location.search.indexOf(PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE) > -1)? VIEW_MODE_WHISTLE : ''; const location = `${window.location.pathname}${window.location.search}${window.location.hash}`; universalRender({history, location, initial_state}) -- GitLab From f5af75e6f7a47165140443f16a54f3e13b294ec4 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 13:21:06 -0600 Subject: [PATCH 053/399] #1989 make header hide of primary importance. --- src/app/components/App.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/components/App.jsx b/src/app/components/App.jsx index ceb90f72d..0d4de9e98 100644 --- a/src/app/components/App.jsx +++ b/src/app/components/App.jsx @@ -129,8 +129,8 @@ class App extends React.Component { depositSteem, signup_bonus, username, nightmodeEnabled, viewMode} = this.props; const lp = false; //location.pathname === '/'; const miniHeader = location.pathname === '/create_account' || location.pathname === '/pick_account'; - const headerHidden = miniHeader && location.search === '?whistle_signup' const whistleView = (viewMode === VIEW_MODE_WHISTLE); + const headerHidden = whistleView; const params_keys = Object.keys(params); const ip = location.pathname === '/' || (params_keys.length === 2 && params_keys[0] === 'order' && params_keys[1] === 'category'); const alert = this.props.error || flash.get('alert') || flash.get('error'); @@ -290,7 +290,7 @@ class App extends React.Component {
    - {miniHeader ? headerHidden ? null : :
    } + {headerHidden ? null : miniHeader ? :
    }
    {welcome_screen} {callout} -- GitLab From f55104c8ce6fa2b6d2800db2a69bfb51374c5127 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 15:45:05 -0600 Subject: [PATCH 054/399] #1989 PickAccount passes view_mode param to enter_email page --- src/app/components/pages/PickAccount.jsx | 15 ++++++++++++--- src/app/utils/Links.js | 16 ++++++++++++++++ src/app/utils/Links.test.js | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/app/components/pages/PickAccount.jsx b/src/app/components/pages/PickAccount.jsx index ce552215e..b05a58c19 100644 --- a/src/app/components/pages/PickAccount.jsx +++ b/src/app/components/pages/PickAccount.jsx @@ -9,6 +9,9 @@ import {api} from 'steem'; import user from 'app/redux/User'; import {validate_account_name} from 'app/utils/ChainValidation'; import runTests from 'app/utils/BrowserTests'; +import {PARAM_VIEW_MODE} from 'shared/constants'; +import {makeParams} from 'app/utils/Links'; + class PickAccount extends React.Component { @@ -19,6 +22,8 @@ class PickAccount extends React.Component { constructor(props) { super(props); + + console.log('PickAccount'); this.state = { name: '', password: '', @@ -48,8 +53,11 @@ class PickAccount extends React.Component { this.setState({server_error: '', loading: true}); const {name} = this.state; if (!name) return; - - window.location = "/enter_email?account=" + name; + const params = {account: name}; + if(this.props.viewMode) { + params[PARAM_VIEW_MODE] = this.props.viewMode; + } + window.location = "/enter_email" + makeParams(params, true); } onPasswordChange(password, password_valid) { @@ -213,7 +221,7 @@ class PickAccount extends React.Component {
    -

    Got an account? Login

    +

    Got an account? Login

    @@ -226,6 +234,7 @@ module.exports = { component: connect( state => { return { + viewMode: state.app.get('viewMode'), loggedIn: !!state.user.get('current'), offchainUser: state.offchain.get('user'), serverBusy: state.offchain.get('serverBusy') diff --git a/src/app/utils/Links.js b/src/app/utils/Links.js index 6b39fe6ed..eb5ac180c 100644 --- a/src/app/utils/Links.js +++ b/src/app/utils/Links.js @@ -35,6 +35,22 @@ export default { ipfsPrefix: /(https?:\/\/.*)?\/ipfs/i, } +//possible this should go somewhere else. +export const makeParams = (params, prefix) => { + let paramsList = []; + if(params.constructor === Array) { + paramsList = params; + } else { + Object.entries(params).forEach(([key, value]) => { + paramsList.push(`${key}=${value}`); + }); + } + if(paramsList.length > 0) { + return ((prefix)? ((typeof prefix === 'string')? prefix : '?') : '') + paramsList.join('&'); + } + return ''; +} + // Original regex // const urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$'; diff --git a/src/app/utils/Links.test.js b/src/app/utils/Links.test.js index 4c84e1702..d53544239 100644 --- a/src/app/utils/Links.test.js +++ b/src/app/utils/Links.test.js @@ -48,6 +48,27 @@ describe('Links', () => { }) }) +describe('makeParams', () => { + it('creates an empty string when there are no params', () => { + assert(linksRe.makeParams([]) === '', 'not empty on array') + assert(linksRe.makeParams({}) === '', 'not empty on object') + assert(linksRe.makeParams({}, true) === '', 'not empty on object with prefix true') + assert(linksRe.makeParams([], true) === '', 'not empty on array with prefix true') + assert(linksRe.makeParams([], '?') === '', 'not empty on array with prefix string') + assert(linksRe.makeParams({}, '?') === '', 'not empty on object with prefix string') + }); + it('creates the correct string when passed an array', () => { + assert(linksRe.makeParams(['bop=boop','troll=bridge']) === 'bop=boop&troll=bridge', 'incorrect string') + assert(linksRe.makeParams(['bop=boop','troll=bridge'], true) === '?bop=boop&troll=bridge', 'incorrect string with prefix true') + assert(linksRe.makeParams(['bop=boop','troll=bridge'], '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &') + }); + it('creates the correct string when passed an object', () => { + assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}) === 'bop=boop&troll=bridge', 'incorrect string') + assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, true) === '?bop=boop&troll=bridge', 'incorrect string with prefix true') + assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &') + }); +}); + // 1st in the browser it is very expensive to re-create a regular expression many times, however, in nodejs is is very in-expensive (it is as if it is caching it). describe('Performance', () => { const largeData = secureRandom.randomBuffer(1024 * 10).toString('hex') -- GitLab From 6a13cc64fc0f4ea5cae5b1adb4bafee688c62da3 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 16:24:04 -0600 Subject: [PATCH 055/399] #1989 more intuitive api for makeParams --- src/app/utils/Links.js | 3 ++- src/app/utils/Links.test.js | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app/utils/Links.js b/src/app/utils/Links.js index eb5ac180c..98dd61ed6 100644 --- a/src/app/utils/Links.js +++ b/src/app/utils/Links.js @@ -46,7 +46,7 @@ export const makeParams = (params, prefix) => { }); } if(paramsList.length > 0) { - return ((prefix)? ((typeof prefix === 'string')? prefix : '?') : '') + paramsList.join('&'); + return ((prefix !== false)? ((typeof prefix === 'string')? prefix : '?') : '') + paramsList.join('&'); } return ''; } @@ -56,3 +56,4 @@ export const makeParams = (params, prefix) => { // About performance // Using exec on the same regex object requires a new regex to be created and compile for each text (ex: post). Instead replace can be used `body.replace(remoteRe, l => {` discarding the result for better performance`}). Re-compiling is a chrome bottleneck but did not effect nodejs. + diff --git a/src/app/utils/Links.test.js b/src/app/utils/Links.test.js index d53544239..4d5f9b43a 100644 --- a/src/app/utils/Links.test.js +++ b/src/app/utils/Links.test.js @@ -52,19 +52,19 @@ describe('makeParams', () => { it('creates an empty string when there are no params', () => { assert(linksRe.makeParams([]) === '', 'not empty on array') assert(linksRe.makeParams({}) === '', 'not empty on object') - assert(linksRe.makeParams({}, true) === '', 'not empty on object with prefix true') - assert(linksRe.makeParams([], true) === '', 'not empty on array with prefix true') + assert(linksRe.makeParams({}, false) === '', 'not empty on object with prefix false') + assert(linksRe.makeParams([], false) === '', 'not empty on array with prefix false') assert(linksRe.makeParams([], '?') === '', 'not empty on array with prefix string') assert(linksRe.makeParams({}, '?') === '', 'not empty on object with prefix string') }); it('creates the correct string when passed an array', () => { - assert(linksRe.makeParams(['bop=boop','troll=bridge']) === 'bop=boop&troll=bridge', 'incorrect string') - assert(linksRe.makeParams(['bop=boop','troll=bridge'], true) === '?bop=boop&troll=bridge', 'incorrect string with prefix true') + assert(linksRe.makeParams(['bop=boop','troll=bridge']) === '?bop=boop&troll=bridge', 'incorrect string with') + assert(linksRe.makeParams(['bop=boop','troll=bridge'], false) === 'bop=boop&troll=bridge', 'incorrect string with prefix false') assert(linksRe.makeParams(['bop=boop','troll=bridge'], '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &') }); it('creates the correct string when passed an object', () => { - assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}) === 'bop=boop&troll=bridge', 'incorrect string') - assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, true) === '?bop=boop&troll=bridge', 'incorrect string with prefix true') + assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}) === '?bop=boop&troll=bridge', 'incorrect string') + assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, false) === 'bop=boop&troll=bridge', 'incorrect string with prefix false') assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &') }); }); -- GitLab From e1fe475d834e692a3e690cf5295d4c2d703e2e61 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 17:19:48 -0600 Subject: [PATCH 056/399] #1989 put addParams in Links for multi-file access --- src/app/utils/Links.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/app/utils/Links.js b/src/app/utils/Links.js index 98dd61ed6..493182a32 100644 --- a/src/app/utils/Links.js +++ b/src/app/utils/Links.js @@ -35,7 +35,24 @@ export default { ipfsPrefix: /(https?:\/\/.*)?\/ipfs/i, } -//possible this should go somewhere else. +//todo: possible this should go somewhere else. +/** + * Returns a new object extended from outputParams with [key] == inputParams[key] if the value is in allowedValues + * @param outputParams + * @param inputParams + * @param key + * @param allowedValues + * @returns {*} + */ +export const addToParams = (outputParams, inputParams, key, allowedValues) => { + const respParams = Object.assign({}, outputParams); + if(inputParams[key] && allowedValues.indexOf(inputParams[key]) > -1) { + respParams[key] = inputParams[key]; + } + return respParams; +} + +//todo: possible this should go somewhere else. export const makeParams = (params, prefix) => { let paramsList = []; if(params.constructor === Array) { -- GitLab From d8252ef21719e370772bc19370b2992840ca20af Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 17:22:13 -0600 Subject: [PATCH 057/399] #1989 support for view_mode param on submit_email route --- .../sign_up_pages/enter_confirm_email.jsx | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/server/sign_up_pages/enter_confirm_email.jsx b/src/server/sign_up_pages/enter_confirm_email.jsx index ffe2b623a..7b7fc5c0f 100644 --- a/src/server/sign_up_pages/enter_confirm_email.jsx +++ b/src/server/sign_up_pages/enter_confirm_email.jsx @@ -4,6 +4,8 @@ import request from "co-request"; import React from "react"; import { renderToString } from "react-dom/server"; import models from "db/models"; +import {PARAM_VIEW_MODE, VIEW_MODE_WHISTLE} from "shared/constants"; +import {addToParams, makeParams} from 'app/utils/Links'; import ServerHTML from "../server-html"; import sendEmail from "../sendEmail"; import { getRemoteIp, checkCSRF } from "server/utils/misc"; @@ -177,17 +179,19 @@ export default function useEnterAndConfirmEmailPages(app) { router.get("/enter_email", function*() { console.log("-- /enter_email -->", this.session.uid, this.session.user, this.request.query.account); + const params = addToParams({}, this.request.query, PARAM_VIEW_MODE, [VIEW_MODE_WHISTLE]); + const viewMode = (params[PARAM_VIEW_MODE]) ? params[PARAM_VIEW_MODE] : ''; const picked_account_name = this.session.picked_account_name = this.request.query.account; if (!picked_account_name) { this.flash = { error: "Please select your account name" }; - this.redirect('/pick_account'); + this.redirect('/pick_account' + makeParams(params)); return; } // check for existing account const check_account_res = yield api.getAccountsAsync([picked_account_name]); if (check_account_res && check_account_res.length > 0) { this.flash = { error: `${picked_account_name} is already taken, please try another name` }; - this.redirect('/pick_account'); + this.redirect('/pick_account' + makeParams(params)); return; } let default_email = ""; @@ -195,12 +199,12 @@ export default function useEnterAndConfirmEmailPages(app) { default_email = this.request.query.email; const body = renderToString(
    - + {(viewMode !== VIEW_MODE_WHISTLE)? : null}
    -
    +

    Your email address, please

    @@ -254,16 +258,17 @@ export default function useEnterAndConfirmEmailPages(app) { router.post("/submit_email", koaBody, function*() { if (!checkCSRF(this, this.request.body.csrf)) return; - + const params = addToParams({}, this.request.query, PARAM_VIEW_MODE, [VIEW_MODE_WHISTLE]); let {email, account} = this.request.body; - console.log('-- /submit_email -->', this.session.uid, email, account); + console.log('-- /submit_email -->', this.session.uid, email, account, this.request.query[PARAM_VIEW_MODE]); + if (!email) { this.flash = { error: "Please provide an email address" }; - this.redirect(`/enter_email?account=${account}`); + this.redirect(`/enter_email?account=${account}` + makeParams(params, '&')); return; } - email = email.trim().toLowerCase(); - account = account.trim().toLowerCase(); + email = params.email = email.trim().toLowerCase(); + account = params.account = account.trim().toLowerCase(); //recaptcha if (config.get('recaptcha.site_key')) { @@ -277,7 +282,7 @@ export default function useEnterAndConfirmEmailPages(app) { this.flash = { error: "Failed captcha verification, please try again" }; - this.redirect(`/enter_email?email=${email}&account=${account}`); + this.redirect(`/enter_email` + makeParams(params)); return; } } @@ -291,7 +296,7 @@ export default function useEnterAndConfirmEmailPages(app) { email ); this.flash = { error: "Not valid email address" }; - this.redirect(`/enter_email?email=${email}&account=${account}`); + this.redirect(`/enter_email` + makeParams(params)); return; } @@ -350,12 +355,12 @@ export default function useEnterAndConfirmEmailPages(app) { } } catch (error) { this.flash = {error: 'Internal Server Error'}; - this.redirect(`/enter_email?email=${email}&account=${account}`); + this.redirect('/enter_email' + + makeParams(params)); console.error('Error in /submit_email :', this.session.uid, error.toString()); } // redirect to phone verification - this.redirect("/enter_mobile"); + this.redirect("/enter_mobile" + makeParams(params)); }); router.get("/confirm_email/:code", confirmEmailHandler); -- GitLab From 891eee620e3feaa128dc222c0eee91cdc3dbb134 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 18:32:10 -0600 Subject: [PATCH 058/399] #1989 pass view_mode param through entire signup. Call .postMessage on success when viewMode is 'whistle' --- src/app/assets/stylesheets/app.scss | 36 +++++++++------- src/app/components/pages/Approval.jsx | 6 ++- src/app/components/pages/PickAccount.jsx | 6 +-- .../sign_up_pages/enter_confirm_email.jsx | 2 +- .../sign_up_pages/enter_confirm_mobile.jsx | 42 ++++++++++++------- src/shared/constants.js | 6 ++- 6 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/app/assets/stylesheets/app.scss b/src/app/assets/stylesheets/app.scss index a1906ee33..da689dd3b 100644 --- a/src/app/assets/stylesheets/app.scss +++ b/src/app/assets/stylesheets/app.scss @@ -62,24 +62,30 @@ a, path, circle { text-transform: uppercase; } +.whistle-view { + .whistle-hidden { + display:none !important; + } +} + .secondary { @include themify($themes) { - color: themed('textColorSecondary'); - } + color: themed('textColorSecondary'); + } font-size: 90%; a { transition: 0.2s all ease-in-out; color: $dark-gray; @include themify($themes) { - color: themed('textColorSecondary'); - } + color: themed('textColorSecondary'); + } :hover { @include themify($themes) { - color: themed('textColorAccent'); - } - } + color: themed('textColorAccent'); + } + } } } @@ -89,15 +95,15 @@ a.ptc { text-decoration: none; transition: 0.2s all ease-in-out; @include themify($themes) { - color: themed('textColorPrimary'); - } + color: themed('textColorPrimary'); + } &:hover, &:focus { color: $dark-gray; text-decoration: none; @include themify($themes) { - color: themed('textColorAccent'); - } + color: themed('textColorAccent'); + } } } @@ -162,7 +168,7 @@ a.ptc { } } -.anchor { +.anchor { padding-top: 68px; margin-top: -68px; position: relative; @@ -180,9 +186,9 @@ h1, h2, h3, h4, h5, h6 { &__module { padding: 1.5em 2em; @include themify($themes) { - background-color: themed('moduleBackgroundColor'); - border: themed('border'); - } + background-color: themed('moduleBackgroundColor'); + border: themed('border'); + } } } diff --git a/src/app/components/pages/Approval.jsx b/src/app/components/pages/Approval.jsx index c4682d83e..2ae8552e9 100644 --- a/src/app/components/pages/Approval.jsx +++ b/src/app/components/pages/Approval.jsx @@ -1,5 +1,6 @@ import React from 'react'; import {connect} from 'react-redux'; +import {VIEW_MODE_WHISTLE, WHISTLE_SIGNUP_COMPLETE} from 'shared/constants'; class Approval extends React.Component { constructor(props) { @@ -16,6 +17,9 @@ class Approval extends React.Component { } render() { + if(process.env.BROWSER && this.props.viewMode === VIEW_MODE_WHISTLE) { + window.postMessage(WHISTLE_SIGNUP_COMPLETE); + } let body = ''; if (this.state.confirm_email) { body =
    @@ -48,7 +52,7 @@ module.exports = { component: connect( state => { return { - + viewMode: state.app.get('viewMode') } }, dispatch => ({ diff --git a/src/app/components/pages/PickAccount.jsx b/src/app/components/pages/PickAccount.jsx index b05a58c19..7ef97f3a5 100644 --- a/src/app/components/pages/PickAccount.jsx +++ b/src/app/components/pages/PickAccount.jsx @@ -14,16 +14,14 @@ import {makeParams} from 'app/utils/Links'; class PickAccount extends React.Component { - static propTypes = { loginUser: React.PropTypes.func.isRequired, serverBusy: React.PropTypes.bool }; + constructor(props) { super(props); - - console.log('PickAccount'); this.state = { name: '', password: '', @@ -57,7 +55,7 @@ class PickAccount extends React.Component { if(this.props.viewMode) { params[PARAM_VIEW_MODE] = this.props.viewMode; } - window.location = "/enter_email" + makeParams(params, true); + window.location = "/enter_email" + makeParams(params); } onPasswordChange(password, password_valid) { diff --git a/src/server/sign_up_pages/enter_confirm_email.jsx b/src/server/sign_up_pages/enter_confirm_email.jsx index 7b7fc5c0f..5095e0d0b 100644 --- a/src/server/sign_up_pages/enter_confirm_email.jsx +++ b/src/server/sign_up_pages/enter_confirm_email.jsx @@ -180,7 +180,7 @@ export default function useEnterAndConfirmEmailPages(app) { router.get("/enter_email", function*() { console.log("-- /enter_email -->", this.session.uid, this.session.user, this.request.query.account); const params = addToParams({}, this.request.query, PARAM_VIEW_MODE, [VIEW_MODE_WHISTLE]); - const viewMode = (params[PARAM_VIEW_MODE]) ? params[PARAM_VIEW_MODE] : ''; + const viewMode = (params[PARAM_VIEW_MODE]) ? params[PARAM_VIEW_MODE] : ''; const picked_account_name = this.session.picked_account_name = this.request.query.account; if (!picked_account_name) { this.flash = { error: "Please select your account name" }; diff --git a/src/server/sign_up_pages/enter_confirm_mobile.jsx b/src/server/sign_up_pages/enter_confirm_mobile.jsx index d52aea6e5..a2560807a 100644 --- a/src/server/sign_up_pages/enter_confirm_mobile.jsx +++ b/src/server/sign_up_pages/enter_confirm_mobile.jsx @@ -3,6 +3,8 @@ import koa_body from "koa-body"; import React from "react"; import { renderToString } from "react-dom/server"; import models from "db/models"; +import {PARAM_VIEW_MODE, VIEW_MODE_WHISTLE} from "shared/constants"; +import {addToParams, makeParams} from 'app/utils/Links'; import ServerHTML from "server/server-html"; // import twilioVerify from "server/utils/twilio"; import teleSignVerify from "server/utils/teleSign"; @@ -40,6 +42,9 @@ const assets = Object.assign({}, require(assets_file), { script: [] }); function* confirmMobileHandler(e) { if (!checkCSRF(this, this.request.body.csrf)) return; + const params = addToParams({}, this.request.query, PARAM_VIEW_MODE, [VIEW_MODE_WHISTLE]); + const enterMobileUrl = `/enter_mobile` + makeParams(params); + const confirmation_code = this.params && this.params.code ? this.params.code : this.request.body.code; @@ -56,7 +61,7 @@ function* confirmMobileHandler(e) { }); if (!user) { this.flash = { error: "User session not found, please make sure you have cookies enabled in your browser for this website" }; - this.redirect("/enter_mobile"); + this.redirect(enterMobileUrl); return; } const mid = yield models.Identity.findOne({ @@ -65,7 +70,7 @@ function* confirmMobileHandler(e) { if (!mid) { this.flash = { error: "Wrong confirmation code" }; - this.redirect("/enter_mobile"); + this.redirect(enterMobileUrl); return; } @@ -73,7 +78,7 @@ function* confirmMobileHandler(e) { if (hours_ago > 24.0) { this.status = 401; this.flash = { error: "Confirmation code has been expired" }; - this.redirect("/enter_mobile"); + this.redirect(enterMobileUrl); return; } @@ -88,7 +93,7 @@ function* confirmMobileHandler(e) { mid.phone ); this.flash = { error: "This phone number has already been used" }; - this.redirect('/enter_mobile'); + this.redirect(enterMobileUrl); return; } @@ -99,7 +104,7 @@ function* confirmMobileHandler(e) { mixpanel.track("SignupStepPhone", { distinct_id: this.session.uid }); console.log("--/Success phone redirecting user", this.session.user); - this.redirect("/approval"); + this.redirect("/approval" + makeParams(params)); } export default function useEnterAndConfirmMobilePages(app) { @@ -113,20 +118,22 @@ export default function useEnterAndConfirmMobilePages(app) { this.session.uid, this.session.user ); + const params = addToParams({}, this.request.query, PARAM_VIEW_MODE, [VIEW_MODE_WHISTLE]); + const viewMode = (params[PARAM_VIEW_MODE]) ? params[PARAM_VIEW_MODE] : ''; const phone = this.query.phone; const country = this.query.country; const body = renderToString(
    - + {(viewMode !== VIEW_MODE_WHISTLE)? : null}

    @@ -136,7 +143,7 @@ export default function useEnterAndConfirmMobilePages(app) {

    We need to send you a quick text.

    - +

    With each Steemit account comes a free initial grant of Steem Power! Phone verification helps cut down on spam accounts.

    @@ -186,15 +193,20 @@ recovery should your account ever be compromised.

    router.post("/submit_mobile", koaBody, function*() { if (!checkCSRF(this, this.request.body.csrf)) return; const user_id = this.session.user; + const country = this.request.body.country; + const localPhone = this.request.body.phone + const params = addToParams({}, this.request.query, PARAM_VIEW_MODE, [VIEW_MODE_WHISTLE]); + const viewMode = (params[PARAM_VIEW_MODE]) ? params[PARAM_VIEW_MODE] : ''; + if (!user_id) { this.flash = { error: "Your session has been interrupted, please start over" }; - this.redirect('/pick_account'); + this.redirect('/pick_account' + makeParams(params)); return; } + params.country = country; + params.phone = localPhone; - const country = this.request.body.country; - const localPhone = this.request.body.phone; - const enterMobileUrl = `/enter_mobile?phone=${localPhone}&country=${country}`; + const enterMobileUrl = `/enter_mobile` + makeParams(params); if (!country || country === "") { this.flash = { error: "Please select a country code" }; @@ -243,7 +255,7 @@ recovery should your account ever be compromised.

    mixpanel.track("SignupStep3", { distinct_id: this.session.uid }); - this.redirect("/approval"); + this.redirect("/approval" + makeParams(params)); return; } yield mid.update({ verified: false, phone }); @@ -307,7 +319,7 @@ recovery should your account ever be compromised.

    const body = renderToString(
    - + {(viewMode !== VIEW_MODE_WHISTLE)? : null}
    @@ -324,7 +336,7 @@ recovery should your account ever be compromised.

    diff --git a/src/shared/constants.js b/src/shared/constants.js index d8a8565e2..f7c4ea1c5 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -1,7 +1,9 @@ -export const VIEW_MODE_WHISTLE = 'whistle'; export const PARAM_VIEW_MODE = 'view_mode'; +export const VIEW_MODE_WHISTLE = 'whistle'; +export const WHISTLE_SIGNUP_COMPLETE = 'whistle_signup_complete'; export default { + PARAM_VIEW_MODE, VIEW_MODE_WHISTLE, - PARAM_VIEW_MODE + WHISTLE_SIGNUP_COMPLETE } -- GitLab From 3c66588b5837e3e73ca638166116f939ef286ee7 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 21 Nov 2017 21:17:06 -0600 Subject: [PATCH 059/399] #1989 - server side viewMode awareness for store.app & handling error on postMessage --- src/app/components/pages/Approval.jsx | 7 ++++++- src/server/app_render.jsx | 9 +++++++-- src/shared/UniversalRender.jsx | 5 +++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/app/components/pages/Approval.jsx b/src/app/components/pages/Approval.jsx index 2ae8552e9..a7d9fce08 100644 --- a/src/app/components/pages/Approval.jsx +++ b/src/app/components/pages/Approval.jsx @@ -18,7 +18,12 @@ class Approval extends React.Component { render() { if(process.env.BROWSER && this.props.viewMode === VIEW_MODE_WHISTLE) { - window.postMessage(WHISTLE_SIGNUP_COMPLETE); + try { + window.postMessage(WHISTLE_SIGNUP_COMPLETE); + } catch(e) { + console.error('Cannot window.postMessage when not in whistle app'); + } + } let body = ''; if (this.state.confirm_email) { diff --git a/src/server/app_render.jsx b/src/server/app_render.jsx index 5d6ccf476..f3a0f25a9 100644 --- a/src/server/app_render.jsx +++ b/src/server/app_render.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { renderToString } from 'react-dom/server'; import Tarantool from 'db/tarantool'; +import {VIEW_MODE_WHISTLE, PARAM_VIEW_MODE} from '../shared/constants'; import ServerHTML from './server-html'; import universalRender from '../shared/UniversalRender'; import models from 'db/models'; @@ -10,7 +11,6 @@ import fs from 'fs'; const path = require('path'); const ROOT = path.join(__dirname, '../..'); - const DB_RECONNECT_TIMEOUT = process.env.NODE_ENV === 'development' ? 1000 * 60 * 60 : 1000 * 60 * 10; function getSupportedLocales() { @@ -111,8 +111,13 @@ async function appRender(ctx) { offchain.recover_account = account_recovery_record.account_name; } } + const initial_state = { + app: { + viewMode: (ctx.request.search.indexOf(PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE) > -1)? VIEW_MODE_WHISTLE : '' + } + } - const { body, title, statusCode, meta } = await universalRender({location: ctx.request.url, store, offchain, ErrorPage, tarantool: Tarantool.instance(), userPreferences}); + const { body, title, statusCode, meta } = await universalRender({initial_state, location: ctx.request.url, store, offchain, ErrorPage, tarantool: Tarantool.instance(), userPreferences}); // Assets name are found in `webpack-stats` file const assets_filename = ROOT + (process.env.NODE_ENV === 'production' ? '/tmp/webpack-stats-prod.json' : '/tmp/webpack-stats-dev.json'); diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index b6bf72380..4f1f04730 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -128,7 +128,8 @@ const onRouterError = (error) => { console.error('onRouterError', error); }; -async function universalRender({ location, initial_state, offchain, ErrorPage, tarantool, userPreferences }) { +async function universalRender({location, initial_state, offchain, ErrorPage, tarantool, userPreferences}) { + let error, redirect, renderProps; try { [error, redirect, renderProps] = await runRouter(location, RootRoute); @@ -247,7 +248,7 @@ async function universalRender({ location, initial_state, offchain, ErrorPage, t offchain.signup_bonus = sdDisp; offchain.server_location = location; - server_store = createStore(rootReducer, { global: onchain, offchain}); + server_store = createStore(rootReducer, { app: initial_state.app, global: onchain, offchain}); server_store.dispatch({type: '@@router/LOCATION_CHANGE', payload: {pathname: location}}); server_store.dispatch({type: 'SET_USER_PREFERENCES', payload: userPreferences}); if (offchain.account) { -- GitLab From 0edfe6ac214ba81b91c771b10f008afbfaa36998 Mon Sep 17 00:00:00 2001 From: Originate Date: Wed, 22 Nov 2017 01:49:12 -0800 Subject: [PATCH 060/399] Further eslint cleanups --- src/app/components/modules/PromotePost.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/components/modules/PromotePost.jsx b/src/app/components/modules/PromotePost.jsx index bee738d7d..67e5ba4b4 100644 --- a/src/app/components/modules/PromotePost.jsx +++ b/src/app/components/modules/PromotePost.jsx @@ -40,8 +40,8 @@ class PromotePost extends Component { onSubmit(e) { e.preventDefault(); - const {author, permlink, onClose} = this.props - const {amount} = this.state + const {author, permlink, onClose} = this.props; + const {amount} = this.state; this.setState({loading: true}); console.log('-- PromotePost.onSubmit -->'); this.props.dispatchSubmit({amount, asset: DEBT_TICKER, author, permlink, onClose, @@ -75,7 +75,7 @@ class PromotePost extends Component {

    {tt('promote_post_jsx.spend_your_DEBT_TOKEN_to_advertise_this_post', {DEBT_TOKEN})}.


    -
    +
    @@ -111,9 +111,9 @@ export default connect( // mapDispatchToProps dispatch => ({ dispatchSubmit: ({amount, asset, author, permlink, currentUser, onClose, errorCallback}) => { - const username = currentUser.get('username') + const username = currentUser.get('username'); const successCallback = () => { - dispatch({type: 'global/GET_STATE', payload: {url: `@${username}/transfers`}}) // refresh transfer history + dispatch({type: 'global/GET_STATE', payload: {url: `@${username}/transfers`}}); // refresh transfer history onClose() } const operation = { -- GitLab From 39dfa0c7a7d0e9fd3a04d5676d462fe4c5c94a70 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Wed, 22 Nov 2017 13:13:37 -0600 Subject: [PATCH 061/399] #1989 a little param cleanup --- src/app/Main.js | 4 +++- src/app/utils/Links.js | 23 +++++++++++++++++-- src/app/utils/Links.test.js | 45 +++++++++++++++++++++++++++---------- src/server/app_render.jsx | 3 ++- src/shared/constants.js | 6 ----- 5 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/app/Main.js b/src/app/Main.js index f9f284f69..c251adcf8 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -9,6 +9,7 @@ import universalRender from 'shared/UniversalRender'; import ConsoleExports from './utils/ConsoleExports'; import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; import * as steem from 'steem'; +import {determineViewMode} from "app/utils/Links"; window.onerror = error => { @@ -89,7 +90,8 @@ function runApp(initial_state) { window.$STM_csrf = initial_state.offchain.csrf; delete initial_state.offchain.csrf; } - initial_state.app.viewMode = (window.location.search.indexOf(PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE) > -1)? VIEW_MODE_WHISTLE : ''; + + initial_state.app.viewMode = determineViewMode(window.location.search); const location = `${window.location.pathname}${window.location.search}${window.location.hash}`; universalRender({history, location, initial_state}) diff --git a/src/app/utils/Links.js b/src/app/utils/Links.js index 493182a32..714fd75ca 100644 --- a/src/app/utils/Links.js +++ b/src/app/utils/Links.js @@ -1,3 +1,4 @@ +import {PARAM_VIEW_MODE, VIEW_MODE_WHISTLE} from "../../shared/constants"; const urlChar = '[^\\s"<>\\]\\[\\(\\)]' const urlCharEnd = urlChar.replace(/\]$/, '.,\']') // insert bad chars to end on @@ -35,7 +36,7 @@ export default { ipfsPrefix: /(https?:\/\/.*)?\/ipfs/i, } -//todo: possible this should go somewhere else. +//TODO: possible this should go somewhere else. /** * Returns a new object extended from outputParams with [key] == inputParams[key] if the value is in allowedValues * @param outputParams @@ -52,7 +53,7 @@ export const addToParams = (outputParams, inputParams, key, allowedValues) => { return respParams; } -//todo: possible this should go somewhere else. +//TODO: possible this should go somewhere else. export const makeParams = (params, prefix) => { let paramsList = []; if(params.constructor === Array) { @@ -68,6 +69,24 @@ export const makeParams = (params, prefix) => { return ''; } +/** + * + * @param {string} search - window.location.search formatted string (may omit '?') + * @returns {string} + */ +export const determineViewMode = (search) => { + const searchList = (search.indexOf('?') === 0) ? search.substr(1).split('&') : search.split('&'); + for(let i = 0; i < searchList.length; i++) { + if(searchList[i].indexOf(PARAM_VIEW_MODE) === 0) { + if(searchList[i] == (PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE)) { //we only want to support known view modes. + return VIEW_MODE_WHISTLE; + } + return ''; + } + } + return ''; +} + // Original regex // const urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$'; diff --git a/src/app/utils/Links.test.js b/src/app/utils/Links.test.js index 4d5f9b43a..48c3bf88c 100644 --- a/src/app/utils/Links.test.js +++ b/src/app/utils/Links.test.js @@ -2,6 +2,7 @@ import assert from 'assert' import secureRandom from 'secure-random' import links, * as linksRe from 'app/utils/Links' +import {PARAM_VIEW_MODE, VIEW_MODE_WHISTLE} from "../../shared/constants"; describe('Links', () => { it('all', () => { @@ -50,22 +51,42 @@ describe('Links', () => { describe('makeParams', () => { it('creates an empty string when there are no params', () => { - assert(linksRe.makeParams([]) === '', 'not empty on array') - assert(linksRe.makeParams({}) === '', 'not empty on object') - assert(linksRe.makeParams({}, false) === '', 'not empty on object with prefix false') - assert(linksRe.makeParams([], false) === '', 'not empty on array with prefix false') - assert(linksRe.makeParams([], '?') === '', 'not empty on array with prefix string') - assert(linksRe.makeParams({}, '?') === '', 'not empty on object with prefix string') + assert(linksRe.makeParams([]) === '', 'not empty on array'); + assert(linksRe.makeParams({}) === '', 'not empty on object'); + assert(linksRe.makeParams({}, false) === '', 'not empty on object with prefix false'); + assert(linksRe.makeParams([], false) === '', 'not empty on array with prefix false'); + assert(linksRe.makeParams([], '?') === '', 'not empty on array with prefix string'); + assert(linksRe.makeParams({}, '?') === '', 'not empty on object with prefix string'); }); it('creates the correct string when passed an array', () => { - assert(linksRe.makeParams(['bop=boop','troll=bridge']) === '?bop=boop&troll=bridge', 'incorrect string with') - assert(linksRe.makeParams(['bop=boop','troll=bridge'], false) === 'bop=boop&troll=bridge', 'incorrect string with prefix false') - assert(linksRe.makeParams(['bop=boop','troll=bridge'], '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &') + assert(linksRe.makeParams(['bop=boop','troll=bridge']) === '?bop=boop&troll=bridge', 'incorrect string with'); + assert(linksRe.makeParams(['bop=boop','troll=bridge'], false) === 'bop=boop&troll=bridge', 'incorrect string with prefix false'); + assert(linksRe.makeParams(['bop=boop','troll=bridge'], '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &'); }); it('creates the correct string when passed an object', () => { - assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}) === '?bop=boop&troll=bridge', 'incorrect string') - assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, false) === 'bop=boop&troll=bridge', 'incorrect string with prefix false') - assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &') + assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}) === '?bop=boop&troll=bridge', 'incorrect string'); + assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, false) === 'bop=boop&troll=bridge', 'incorrect string with prefix false'); + assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &'); + }); +}); + +describe('determineViewMode', () => { + it('returns empty string when no parameter in search', () => { + assert(linksRe.determineViewMode('') === '', linksRe.determineViewMode('') + 'not empty on empty string'); + assert(linksRe.determineViewMode('?afs=asdf') === '', 'not empty on incorrect parameter'); + assert(linksRe.determineViewMode('?afs=asdf&apple=sauce') === '', 'not empty on incorrect parameter'); + }); + + it('returns empty string when unrecognized value for parameter in search', () => { + assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=asd`) === '', 'not empty on incorrect parameter value'); + assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}1`) === '', 'not empty on incorrect parameter value'); + assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=asdf&apple=sauce`) === '', 'not empty on incorrect parameter value'); + assert(linksRe.determineViewMode(`?apple=sauce&${PARAM_VIEW_MODE}=asdf`) === '', 'not empty on incorrect parameter value'); + }); + it('returns correct value when recognized value for parameter in search', () => { + assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}`) === VIEW_MODE_WHISTLE, 'wrong response on correct parameter'); + assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}&apple=sauce`) === VIEW_MODE_WHISTLE, 'wrong response on correct parameter'); + assert(linksRe.determineViewMode(`?apple=sauce&${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}`) === VIEW_MODE_WHISTLE, 'wrong response on correct parameter'); }); }); diff --git a/src/server/app_render.jsx b/src/server/app_render.jsx index f3a0f25a9..ef2b9043c 100644 --- a/src/server/app_render.jsx +++ b/src/server/app_render.jsx @@ -8,6 +8,7 @@ import models from 'db/models'; import secureRandom from 'secure-random'; import ErrorPage from 'server/server-error'; import fs from 'fs'; +import {determineViewMode} from "../app/utils/Links"; const path = require('path'); const ROOT = path.join(__dirname, '../..'); @@ -113,7 +114,7 @@ async function appRender(ctx) { } const initial_state = { app: { - viewMode: (ctx.request.search.indexOf(PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE) > -1)? VIEW_MODE_WHISTLE : '' + viewMode: determineViewMode(ctx.request.search) } } diff --git a/src/shared/constants.js b/src/shared/constants.js index f7c4ea1c5..c7ea4f682 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -1,9 +1,3 @@ export const PARAM_VIEW_MODE = 'view_mode'; export const VIEW_MODE_WHISTLE = 'whistle'; export const WHISTLE_SIGNUP_COMPLETE = 'whistle_signup_complete'; - -export default { - PARAM_VIEW_MODE, - VIEW_MODE_WHISTLE, - WHISTLE_SIGNUP_COMPLETE -} -- GitLab From f627ead1980f73cd67585006a9abe72327a55516 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Wed, 22 Nov 2017 14:01:59 -0600 Subject: [PATCH 062/399] #1989 remove try/catch from in-whistle postMessage call --- src/app/components/pages/Approval.jsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/app/components/pages/Approval.jsx b/src/app/components/pages/Approval.jsx index a7d9fce08..2ae8552e9 100644 --- a/src/app/components/pages/Approval.jsx +++ b/src/app/components/pages/Approval.jsx @@ -18,12 +18,7 @@ class Approval extends React.Component { render() { if(process.env.BROWSER && this.props.viewMode === VIEW_MODE_WHISTLE) { - try { - window.postMessage(WHISTLE_SIGNUP_COMPLETE); - } catch(e) { - console.error('Cannot window.postMessage when not in whistle app'); - } - + window.postMessage(WHISTLE_SIGNUP_COMPLETE); } let body = ''; if (this.state.confirm_email) { -- GitLab From 368bd9334fe12fd63d1fe458eb6dc5b6947be586 Mon Sep 17 00:00:00 2001 From: TimCliff Date: Thu, 23 Nov 2017 18:22:23 -0500 Subject: [PATCH 063/399] Fix z-index of anchors for #1940 --- src/app/assets/stylesheets/app.scss | 1 + src/app/help/en/faq.md | 56 ++++++++++------------------- src/app/help/en/welcome.md | 6 ++-- 3 files changed, 22 insertions(+), 41 deletions(-) diff --git a/src/app/assets/stylesheets/app.scss b/src/app/assets/stylesheets/app.scss index da689dd3b..7be2447ac 100644 --- a/src/app/assets/stylesheets/app.scss +++ b/src/app/assets/stylesheets/app.scss @@ -173,6 +173,7 @@ a.ptc { margin-top: -68px; position: relative; display: block; + z-index:-1000; } h1, h2, h3, h4, h5, h6 { diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index a0fde71a6..2aeb9973b 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1,10 +1,8 @@ # Steemit FAQ - -## Table of Contents +## Table of Contents - -### General +### General - What is Steemit.com? - How does Steemit work? - How does Steemit differ from other social media websites? @@ -14,8 +12,7 @@ - Where does the value come from? - Why are people getting vastly different rewards? - -### Accounts +### Accounts - How do I create an account? - What information do I need to provide in order to create an account? - How long does the account approval process take? @@ -27,12 +24,11 @@ - Am I allowed to create more than one account - Can I delete or deactivate my account? -### Community +### Community - Is there an Etiquette Guide for Steemit? - Am I required to verify my identity? - -### Site Navigation +### Site Navigation - How do I upvote a post or comment? - What do the Home, New, Hot, Trending, and Promoted links show? - What information is available in my account menu? @@ -51,8 +47,7 @@ - Can I see the list of users I am following, and who is following me? - What languages are supported? - -### Posting +### Posting - What can users post to Steemit? - What are the different choices for post rewards (50%/50%, Power Up 100%, Decline Payout)? - How do I add images and photos to my posts? @@ -72,13 +67,11 @@ - What does "Promoting" a post do? - How do I promote a post? - -### Comments +### Comments - Can I earn digital tokens for commenting? - How often can I comment? - -### Economics +### Economics - Where do the new STEEM tokens come from? - How many new tokens are generated by the blockchain? - How are the new tokens distributed? @@ -107,8 +100,7 @@ - Are there fees for Powering Up, Powering Down, trading on the internal market, or converting SBD to STEEM? - How long does it take to transfer STEEM or SBD tokens between users? - -### Voting and Curating +### Voting and Curating - What is my voting power? - How many times can I vote without depleting my voting power? - Can I vote with less than 100% of my voting strength? @@ -124,29 +116,25 @@ - Will a downvote hurt my reputation? - What is the difference between a downvote and a flag? - -### Plagiarism, Spam, and Abuse +### Plagiarism, Spam, and Abuse - What are Steemit’s policies on plagiarism and spam? - Is it okay to use random pictures from the internet? - What is Steemcleaners? - What is @cheetah? - Where do I report a post or comment that contains plagiarism, spam, or abuse? - -### Reputation +### Reputation - What is Reputation? - How is the Reputation score measured? - How do I improve my reputation score? - What causes my reputation score to go down? - Why does my reputation score matter? - -### Followers, Feeds, and Resteem +### Followers, Feeds, and Resteem - What is Resteeming? - Can I share on other social media? - -### Blockchain +### Blockchain - What is a blockchain? - What is the Steem blockchain? - What is the difference between Steem and Steemit? @@ -157,8 +145,7 @@ - Where can I find the information for the official launch of the blockchain? - Can I mine STEEM? - -### Steemit, Inc. +### Steemit, Inc. - Who is the CEO of Steemit? - Can I invest in Steemit? - What does Steemit’s development roadmap look like? @@ -167,8 +154,7 @@ - Did Steemit "pre-mine" tokens? - What is the Steemit Privacy Policy? - -### Security +### Security - How can I keep my Steem account secure? - Why should I be careful with my master password? - Why is the master password a long string of gibberish? @@ -179,30 +165,26 @@ - How does the stolen account recovery process work? - How do I report a security vulnerability? - -### Developers +### Developers - Are the Steem blockchain and Steemit.com code open-source? - Is there a Github page for Steemit.com? - Is there a Github page for the Steem blockchain? - What is available for developers interested in Steem and Steemit? - How do I use cli_wallet? - -### Witnesses +### Witnesses - What are Steem witnesses? - How can I vote for witnesses? - How many witnesses can I vote for? - -### Miscellaneous +### Miscellaneous - What third-party tools are there for Steemit? - Is there an official Steemit Facebook page? - Is there an official Steemit Twitter account? - What is the Steem Whitepaper and what is its purpose? - Where can I ask for help if my question was not answered here? - -### Disclaimer +### Disclaimer - Third Party References and User Links # General diff --git a/src/app/help/en/welcome.md b/src/app/help/en/welcome.md index a94d1fc42..3ddb28830 100644 --- a/src/app/help/en/welcome.md +++ b/src/app/help/en/welcome.md @@ -8,8 +8,7 @@ Below that is a section of "Helpful Posts from Steemit Users", which contains a Below that is a list of recommended users to follow, a collection of other resources including the FAQ Page, and information on where to find live help. - -## Table of Contents +## Table of Contents ### Quick Start Guide @@ -303,8 +302,7 @@ It is not required either, but if you have other social media accounts (Twitter, *** - -## Helpful Posts from Steemit Users +## Helpful Posts from Steemit Users - [Posting and Markdown Basics](https://steemit.com/steemit/@thecryptofiend/markdown-basics-for-beginners) - [Tons of Ways to Spend Your Hard Earned STEEM/SBD](https://steemit.com/steem/@timcliff/the-steem-economy-tons-of-ways-to-spend-your-hard-earned-steem-sbd) -- GitLab From c8443dc23442ebdbc983064299ad303db1b4bc1f Mon Sep 17 00:00:00 2001 From: plink01001 Date: Sat, 25 Nov 2017 23:24:56 -0500 Subject: [PATCH 064/399] Add SMT Whitepaper to menu - solves #2052 --- src/app/components/App.jsx | 5 +++++ src/app/locales/en.json | 1 + src/app/locales/es.json | 1 + src/app/locales/fr.json | 1 + src/app/locales/it.json | 1 + src/app/locales/ru.json | 1 + 6 files changed, 10 insertions(+) diff --git a/src/app/components/App.jsx b/src/app/components/App.jsx index 0d4de9e98..22996682b 100644 --- a/src/app/components/App.jsx +++ b/src/app/components/App.jsx @@ -268,6 +268,11 @@ class App extends React.Component { {tt('navigation.bluepaper')}  +
  • + + {tt('navigation.smt_whitepaper')}  + +
  • {tt('navigation.whitepaper')}  diff --git a/src/app/locales/en.json b/src/app/locales/en.json index 37b0e9b78..78427ea56 100644 --- a/src/app/locales/en.json +++ b/src/app/locales/en.json @@ -238,6 +238,7 @@ "app_center": "Steemit App Center", "api_docs": "Steemit API Docs", "bluepaper": "Steem Bluepaper", + "smt_whitepaper": "SMT Whitepaper", "whitepaper": "Steem Whitepaper", "intro_tagline": "Money talks", "intro_paragraph": "Your voice is worth something. Join the community that pays you to post and curate high quality content." diff --git a/src/app/locales/es.json b/src/app/locales/es.json index c6867951a..a950c76db 100644 --- a/src/app/locales/es.json +++ b/src/app/locales/es.json @@ -233,6 +233,7 @@ "app_center": "Centro de Aplicaciones Steemit", "api_docs": "Documentos API de Steemit", "bluepaper": "Steem Bluepaper", + "smt_whitepaper": "Libro blanco de SMT", "whitepaper": "Libro blanco de Steem", "intro_tagline": "El dinero habla.", "intro_paragraph": "Tu voz tiene valor. Únete a la comunidad que te paga por publicar y votar contenido de alta calidad" diff --git a/src/app/locales/fr.json b/src/app/locales/fr.json index 1c3d3f1ca..9df8c6b3d 100644 --- a/src/app/locales/fr.json +++ b/src/app/locales/fr.json @@ -234,6 +234,7 @@ "api_docs": "Documentation API sur Steemit", "whitepaper": "Livre blanc sur le Steem", "bluepaper": "Steem Bluepaper", + "smt_whitepaper": "Livre blanc sur le SMT", "intro_tagline": "Présentations sur la monnaie", "intro_paragraph": "Votre voix vaut quelque chose. Rejoignez la communauté qui vous rémunère pour vos articles et participez à la curation du contenu de qualité." }, diff --git a/src/app/locales/it.json b/src/app/locales/it.json index 74464332c..8c6b386b2 100644 --- a/src/app/locales/it.json +++ b/src/app/locales/it.json @@ -233,6 +233,7 @@ "app_center": "Steemit App Center", "api_docs": "Steemit API Docs", "bluepaper": "Steem Bluepaper", + "smt_whitepaper": "SMT Whitepaper", "whitepaper": "Steem Whitepaper", "intro_tagline": "Il denaro parla", "intro_paragraph": "La tua voce vale qualcosa. Entra nella comunità che ti pagare per pubblicare e curare contenuti di alta qualità." diff --git a/src/app/locales/ru.json b/src/app/locales/ru.json index 4fdad0b52..ddf516859 100644 --- a/src/app/locales/ru.json +++ b/src/app/locales/ru.json @@ -239,6 +239,7 @@ "app_center": "Центр приложений Steemit", "api_docs": "API-документация Steemit", "bluepaper": "Синяя бумага Steem", + "smt_whitepaper": "Белая книга SMT", "whitepaper": "Белая книга Steem", "intro_tagline": "Здесь говорят деньги.", "intro_paragraph": "Ваше мнение имеет цену. Присоединяйтесь к сообществу, которое платит за контент и за работу по отбору самого лучшего контента." -- GitLab From c100376c79ce58a1fc085e642fda07f1f78b9719 Mon Sep 17 00:00:00 2001 From: Ian Park Date: Mon, 27 Nov 2017 14:24:47 +0000 Subject: [PATCH 065/399] Adding the missing translation (#2057) Based on the comment from valzav, there are new items in the locale JSON file, so I've added those to ko.json missing translation: ko.g.myblog missing translation: ko.g.mycomments missing translation: ko.g.myreplies --- src/app/locales/ko.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/locales/ko.json b/src/app/locales/ko.json index dbad83253..c5085c60b 100644 --- a/src/app/locales/ko.json +++ b/src/app/locales/ko.json @@ -43,6 +43,9 @@ "logout": "로그아웃", "memo": "메모", "mute": "뮤트", + "myblog": "내블로그", + "mycomments": "나의댓글", + "myreplies": "내게달린댓글", "new": "최신글", "newer": "Newer", "next": "Next", -- GitLab From 3ffd06aa952aa0e8a21b62c85772ee1507da4423 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Mon, 27 Nov 2017 10:18:31 -0600 Subject: [PATCH 066/399] 2046 correcting bad use of a tag --- src/app/assets/stylesheets/app.scss | 1 + src/app/components/cards/PostSummary.jsx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/assets/stylesheets/app.scss b/src/app/assets/stylesheets/app.scss index da689dd3b..5649e539c 100644 --- a/src/app/assets/stylesheets/app.scss +++ b/src/app/assets/stylesheets/app.scss @@ -92,6 +92,7 @@ a, path, circle { .ptc, a.ptc { + cursor:pointer; text-decoration: none; transition: 0.2s all ease-in-out; @include themify($themes) { diff --git a/src/app/components/cards/PostSummary.jsx b/src/app/components/cards/PostSummary.jsx index 8c5af2ec7..af071b0bb 100644 --- a/src/app/components/cards/PostSummary.jsx +++ b/src/app/components/cards/PostSummary.jsx @@ -81,7 +81,7 @@ class PostSummary extends React.Component {

    - {tt('postsummary_jsx.resteemed')} + {tt('postsummary_jsx.resteemed')}

    ) } @@ -91,7 +91,7 @@ class PostSummary extends React.Component { reblogged_by = (

    - {tt('postsummary_jsx.resteemed')} + {tt('postsummary_jsx.resteemed')}

    ) } @@ -199,7 +199,7 @@ class PostSummary extends React.Component {
    {summary_header} - nsfw  {tt('postsummary_jsx.reveal_it')} {tt('g.or') + ' '} + nsfw  {tt('postsummary_jsx.reveal_it')} {tt('g.or') + ' '} {username ? {tt('postsummary_jsx.adjust_your')} {tt('postsummary_jsx.display_preferences')}. : {tt('postsummary_jsx.create_an_account')} {tt('postsummary_jsx.to_save_your_preferences')}.} -- GitLab From ca1ed4409e9f6a2769bba4246f9fa388859dd412 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Mon, 27 Nov 2017 11:14:19 -0600 Subject: [PATCH 067/399] 2046 remove post modal & associated code --- src/app/components/cards/PostSummary.jsx | 24 ++------- src/app/components/cards/PostsList.jsx | 62 ++---------------------- 2 files changed, 8 insertions(+), 78 deletions(-) diff --git a/src/app/components/cards/PostSummary.jsx b/src/app/components/cards/PostSummary.jsx index af071b0bb..75c83f1f0 100644 --- a/src/app/components/cards/PostSummary.jsx +++ b/src/app/components/cards/PostSummary.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Link, browserHistory } from 'react-router'; +import { Link } from 'react-router'; import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper'; import Icon from 'app/components/elements/Icon'; import { connect } from 'react-redux'; @@ -18,21 +18,6 @@ import ImageUserBlockList from 'app/utils/ImageUserBlockList'; import proxifyImageUrl from 'app/utils/ProxifyUrl'; import Userpic, { avatarSize } from 'app/components/elements/Userpic'; -function isLeftClickEvent(event) { - return event.button === 0 -} - -function isModifiedEvent(event) { - return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) -} - -function navigate(e, onClick, post, url) { - if (isModifiedEvent(e) || !isLeftClickEvent(e)) return; - e.preventDefault(); - if (onClick) onClick(post, url); - else browserHistory.push(url); -} - class PostSummary extends React.Component { static propTypes = { post: React.PropTypes.string.isRequired, @@ -41,7 +26,6 @@ class PostSummary extends React.Component { content: React.PropTypes.object.isRequired, thumbSize: React.PropTypes.string, nsfwPref: React.PropTypes.string, - onClick: React.PropTypes.func }; constructor() { @@ -66,7 +50,7 @@ class PostSummary extends React.Component { } render() { - const {thumbSize, ignore, onClick} = this.props; + const {thumbSize, ignore} = this.props; const {post, content} = this.props; const {account} = this.props; if (!content) return null; @@ -224,7 +208,7 @@ class PostSummary extends React.Component { if (this.props.blogmode) { thumb = ( - navigate(e, onClick, post, p.link)} className="articles__feature-img-container"> + ); @@ -232,7 +216,7 @@ class PostSummary extends React.Component { const listSize = proxifyImageUrl(p.image_link, '256x512').replace(/ /g, '%20'); thumb = ( - navigate(e, onClick, post, p.link)} className="articles__feature-img-container"> + diff --git a/src/app/components/cards/PostsList.jsx b/src/app/components/cards/PostsList.jsx index 161529349..751a5c193 100644 --- a/src/app/components/cards/PostsList.jsx +++ b/src/app/components/cards/PostsList.jsx @@ -32,17 +32,16 @@ class PostsList extends React.Component { static defaultProps = { showSpam: false, - } + loading: false + }; constructor() { super(); this.state = { thumbSize: 'desktop', - showNegativeComments: false, - showPost: null + showNegativeComments: false } this.scrollListener = this.scrollListener.bind(this); - this.onPostClick = this.onPostClick.bind(this); this.onBackButton = this.onBackButton.bind(this); this.closeOnOutsideClick = this.closeOnOutsideClick.bind(this); this.shouldComponentUpdate = shouldComponentUpdate(this, 'PostsList') @@ -52,31 +51,6 @@ class PostsList extends React.Component { this.attachScrollListener(); } - componentWillUpdate() { - const location = `${window.location.pathname}${window.location.search}${window.location.hash}`; - if (this.state.showPost && (location !== this.post_url)) { - this.setState({showPost: null}); - } - } - - componentDidUpdate(prevProps, prevState) { - if (this.state.showPost && !prevState.showPost) { - document.getElementsByTagName('body')[0].className = 'with-post-overlay'; - window.addEventListener('popstate', this.onBackButton); - window.addEventListener('keydown', this.onBackButton); - const post_overlay = document.getElementById('post_overlay'); - if (post_overlay) { - post_overlay.addEventListener('click', this.closeOnOutsideClick); - post_overlay.focus(); - } - } - if (!this.state.showPost && prevState.showPost) { - window.history.pushState({}, '', this.props.pathname); - document.getElementsByTagName('body')[0].className = ''; - this.post_url = null; - } - } - componentWillUnmount() { this.detachScrollListener(); window.removeEventListener('popstate', this.onBackButton); @@ -90,7 +64,6 @@ class PostsList extends React.Component { if ('keyCode' in e && e.keyCode !== 27) return; window.removeEventListener('popstate', this.onBackButton); window.removeEventListener('keydown', this.onBackButton); - this.closePostModal(); } closeOnOutsideClick(e) { @@ -105,11 +78,6 @@ class PostsList extends React.Component { } } - closePostModal = () => { - window.document.title = this.state.prevTitle; - this.setState({showPost: null, prevTitle: null}); - } - fetchIfNeeded() { this.scrollListener(); } @@ -150,18 +118,10 @@ class PostsList extends React.Component { window.removeEventListener('resize', this.scrollListener); } - onPostClick(post, url) { - this.post_url = url; - this.props.fetchState(url); - this.props.removeHighSecurityKeys(); - this.setState({showPost: post, prevTitle: window.document.title}); - window.history.pushState({}, '', url); - } - render() { const {posts, showSpam, loading, category, content, ignore_result, account, nsfwPref} = this.props; - const {thumbSize, showPost} = this.state + const {thumbSize} = this.state; const postsInfo = []; posts.forEach((item) => { const cont = content.get(item); @@ -180,7 +140,6 @@ class PostsList extends React.Component { post={item.item} thumbSize={thumbSize} ignore={item.ignore} - onClick={this.onPostClick} nsfwPref={nsfwPref} />
  • ) @@ -191,19 +150,6 @@ class PostsList extends React.Component { {renderSummary(postsInfo)} {loading &&
    } - {showPost &&
    - -
    - -
    -
    }
    ); } -- GitLab From 75dece1bd117fe4b63e9d6a54fa03f4596ac8c9e Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Mon, 27 Nov 2017 11:24:56 -0600 Subject: [PATCH 068/399] #1712 remove unnecessary 'run' in yarn commands --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 004867113..d7b0157f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,7 @@ COPY . /var/app # ./node_modules/.bin/eslint . && \ # npm run build -RUN yarn run test && yarn run build +RUN yarn test && yarn build ENV PORT 8080 ENV NODE_ENV production -- GitLab From 228e57eee75aedec2b54c639511be04276dc9491 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 28 Nov 2017 11:18:54 -0600 Subject: [PATCH 069/399] node 8.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 99b8d4e46..96ca90233 100644 --- a/package.json +++ b/package.json @@ -183,7 +183,7 @@ "fsevents": "*" }, "engines": { - "node": ">=7.0.0", + "node": ">=8.7.0", "npm": ">=5.4.2" }, "pre-commit": [ -- GitLab From 827f14f1abf644769a868d2e02ee1879f2cd0df3 Mon Sep 17 00:00:00 2001 From: roadscape Date: Tue, 28 Nov 2017 11:36:31 -0600 Subject: [PATCH 070/399] Revert "move npm commands to yarn in Dockerfile" --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d7b0157f9..4cc9e00cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,9 @@ COPY . /var/app # ./node_modules/.bin/eslint . && \ # npm run build -RUN yarn test && yarn build +RUN mkdir tmp && \ + npm test && \ + npm run-script build ENV PORT 8080 ENV NODE_ENV production -- GitLab From 1a13fae1a8853ec8edd21c2ef6f029436d6aa8a5 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 28 Nov 2017 11:38:40 -0600 Subject: [PATCH 071/399] update docker node --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d7b0157f9..5ee9e36c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:7.5 +FROM node:8.7 # yarn > npm #RUN npm install --global yarn -- GitLab From c293cd16ccb9243c97441dea13f43426d3e20f34 Mon Sep 17 00:00:00 2001 From: Johan Nordberg Date: Tue, 28 Nov 2017 20:39:08 +0100 Subject: [PATCH 072/399] Bump steemjs to 0.6.7 --- package.json | 2 +- yarn.lock | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index e8dfe494f..c57b82bc3 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "sequelize-cli": "^2.3.1", "speakingurl": "^9.0.0", "sqlite3": "^3.1.8", - "@steemit/steem-js": "0.6.5", + "@steemit/steem-js": "0.6.7", "store": "^1.3.20", "style-loader": "^0.18.2", "svg-inline-loader": "^0.8.0", diff --git a/yarn.lock b/yarn.lock index 81fee0129..060157479 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,9 +10,9 @@ libsodium "^0.4.8" libsodium-wrappers "^0.4.8" -"@steemit/steem-js@0.6.5": - version "0.6.5" - resolved "https://registry.yarnpkg.com/@steemit/steem-js/-/steem-js-0.6.5.tgz#a571eed61c71dd9bbae411da195732c9aaf34f3b" +"@steemit/steem-js@0.6.7": + version "0.6.7" + resolved "https://registry.yarnpkg.com/@steemit/steem-js/-/steem-js-0.6.7.tgz#ac78153bb92bc6c652898aabf2c4a0de220b6c00" dependencies: bigi "^1.4.2" bluebird "^3.4.6" @@ -23,10 +23,10 @@ create-hash "^1.1.2" create-hmac "^1.1.4" cross-env "^5.0.0" + cross-fetch "^1.1.1" debug "^2.6.8" detect-node "^2.0.3" ecurve "^1.0.5" - fetch-ponyfill "^4.1.0" lodash "^4.16.4" postinstall-build "^5.0.1" secure-random "^1.1.1" @@ -1968,6 +1968,13 @@ cross-env@^5.0.0: cross-spawn "^5.1.0" is-windows "^1.0.0" +cross-fetch@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-1.1.1.tgz#dede6865ae30f37eae62ac90ebb7bdac002b05a0" + dependencies: + node-fetch "1.7.3" + whatwg-fetch "2.0.3" + cross-spawn@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" @@ -3021,12 +3028,6 @@ fbjs@^0.8.1, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9, fbjs@~0: setimmediate "^1.0.5" ua-parser-js "^0.7.9" -fetch-ponyfill@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" - dependencies: - node-fetch "~1.7.1" - figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -5430,7 +5431,7 @@ nocache@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.0.0.tgz#202b48021a0c4cbde2df80de15a17443c8b43980" -node-fetch@^1.0.1, node-fetch@~1.7.1: +node-fetch@1.7.3, node-fetch@^1.0.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" dependencies: @@ -8323,6 +8324,10 @@ whatwg-encoding@^1.0.1: dependencies: iconv-lite "0.4.13" +whatwg-fetch@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" + whatwg-fetch@>=0.10.0, whatwg-fetch@^0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.11.1.tgz#6d3ded245fdd97cd728e0e2587b54b733949e663" -- GitLab From 9f9834878c40e16eee53918086db360aa2476aeb Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 28 Nov 2017 13:50:52 -0600 Subject: [PATCH 073/399] put back npm->yarn docker command #1712 --- Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index c51561f0f..5ee9e36c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,9 +19,7 @@ COPY . /var/app # ./node_modules/.bin/eslint . && \ # npm run build -RUN mkdir tmp && \ - npm test && \ - npm run-script build +RUN yarn test && yarn build ENV PORT 8080 ENV NODE_ENV production -- GitLab From a95e3848834ffe281203b7d1a975778bfe7ee667 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 28 Nov 2017 14:23:36 -0600 Subject: [PATCH 074/399] Add tmp dir --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5ee9e36c3..693f8e618 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,8 @@ COPY . /var/app # ./node_modules/.bin/eslint . && \ # npm run build -RUN yarn test && yarn build +RUN mkdir tmp && \ + yarn test && yarn build ENV PORT 8080 ENV NODE_ENV production -- GitLab From 29146a3179e0d41548c0ff4bc6ad2959e8a93ecf Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 28 Nov 2017 15:50:06 -0600 Subject: [PATCH 075/399] Closes #1981 - Use a frozen lockfile when building in docker --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 693f8e618..1c2adfb50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN npm install -g yarn WORKDIR /var/app RUN mkdir -p /var/app ADD package.json /var/app/package.json -RUN yarn +RUN yarn install --non-interactive --frozen-lockfile COPY . /var/app -- GitLab From dea1f774593354d53758a6ad1782129cb1ae0831 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Tue, 28 Nov 2017 16:42:22 -0600 Subject: [PATCH 076/399] nudge dockerhub --- src/shared/UniversalRender.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index adff7b7ed..ae4cd9401 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -78,7 +78,7 @@ const scrollTop = (el, topOffset, prevDocumentInfo, triesRemaining) => { scrollTopTimeout = setTimeout(() => scrollTop(el, topOffset, documentInfo, (triesRemaining-1)), SCROLL_TOP_DELAY_MS); } } -} +}; /** * raison d'être: on hash link navigation, calculate the appropriate y-scroll with a fixed position top menu -- GitLab From d6cb7e43eeb8fcc1185061aa7d6a93e2fdb19a0c Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Tue, 28 Nov 2017 19:36:29 -0500 Subject: [PATCH 077/399] change ref from old steemjs lib to new one --- webpack/base.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack/base.config.js b/webpack/base.config.js index c02db30b9..0997e426e 100644 --- a/webpack/base.config.js +++ b/webpack/base.config.js @@ -39,7 +39,7 @@ export default { 'react', 'react-dom', 'react-router', - 'steem', + '@steemit/steem-js', 'bytebuffer', 'immutable', 'autolinker', -- GitLab From ed32bb9361ddbc3d6047de49abe7b4c16d734f6e Mon Sep 17 00:00:00 2001 From: TimCliff Date: Tue, 28 Nov 2017 22:22:14 -0600 Subject: [PATCH 078/399] Add @bellyrub to BadActorList for issue #2063 Currently the BadActorList seems to have been used for accounts that were setup to receive funds from users that misspelled commonly used accounts. In issue https://github.com/steemit/condenser/issues/2063 it was reported that @bellyrub has been stealing user's funds. I don't know if this is the purpose that BadActorList was setup for, but it seems like a good reason to add the account. --- src/app/utils/BadActorList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index 7a3138d77..cbf9a23a7 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -96,6 +96,7 @@ shapeshift randomwhale randowale randwhale +bellyrub coinpayments minnowboost minnowboster -- GitLab From 6045b00b51d475d96cb2e8096481eaf96930a45c Mon Sep 17 00:00:00 2001 From: Iain Maitland Date: Sat, 25 Nov 2017 22:16:42 -0500 Subject: [PATCH 079/399] purge vestigal components, refs #2059 --- src/app/components/all.scss | 1 - .../components/elements/ExperimentWrapper.jsx | 82 ------------------- .../components/elements/LocalizedCurrency.jsx | 6 -- src/app/components/elements/SaveLogin.jsx | 42 ---------- .../components/elements/SignupProgressBar.jsx | 17 ---- .../elements/SignupProgressBar.scss | 73 ----------------- src/app/components/elements/Template.jsx | 72 ---------------- src/app/components/pages/Post.jsx | 3 +- 8 files changed, 1 insertion(+), 295 deletions(-) delete mode 100644 src/app/components/elements/ExperimentWrapper.jsx delete mode 100644 src/app/components/elements/LocalizedCurrency.jsx delete mode 100644 src/app/components/elements/SaveLogin.jsx delete mode 100644 src/app/components/elements/SignupProgressBar.jsx delete mode 100644 src/app/components/elements/SignupProgressBar.scss delete mode 100644 src/app/components/elements/Template.jsx diff --git a/src/app/components/all.scss b/src/app/components/all.scss index e5b33d83f..ca5167f61 100644 --- a/src/app/components/all.scss +++ b/src/app/components/all.scss @@ -26,7 +26,6 @@ @import "./elements/Reputation"; @import "./elements/Reblog"; @import "./elements/YoutubePreview"; -@import "./elements/SignupProgressBar"; @import "./elements/ShareMenu"; @import "./elements/Author"; @import "./elements/AuthorDropdown"; diff --git a/src/app/components/elements/ExperimentWrapper.jsx b/src/app/components/elements/ExperimentWrapper.jsx deleted file mode 100644 index aaa3b833a..000000000 --- a/src/app/components/elements/ExperimentWrapper.jsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, {PropTypes} from 'react'; -import {connect} from 'react-redux'; -import Experiment from 'react-ab-test/lib/Experiment'; -import emitter from 'react-ab-test/lib/emitter'; -import experimentDebugger from 'react-ab-test/lib/debugger'; -import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; - -/* -Usage example: - -import Experiment from 'app/components/elements/ExperimentWrapper'; - -win() { - Experiment.win('MyExample1'); -} - - - -
    Section A
    -
    - -
    Section B
    -
    - -
    Section C
    -
    - -
    Section D
    -
    - -
    Section E
    -
    - -
    Section F
    -
    -
    - */ - -class ExperimentWrapper extends React.Component { - static propTypes = { - name: PropTypes.string.isRequired, - uid: PropTypes.string.isRequired, - children: PropTypes.any.isRequired - }; - - constructor(props) { - super(props); - experimentDebugger.enable(); - } - - render() { - const uid = this.props.uid; - return ( - - {this.props.children} - - ); - } -} - -emitter.addPlayListener( (experimentName, variantName) => { - // console.log("Displaying experiment ‘" + experimentName + "’ variant ‘" + variantName + "’"); - serverApiRecordEvent('Ex:' + experimentName, 'Show:' + variantName, 10); -}); - -emitter.addWinListener( (experimentName, variantName) => { - // console.log("Variant ‘" + variantName + "’ of experiment ‘" + experimentName + "’ was clicked"); - serverApiRecordEvent('Ex: ' + experimentName, 'Win:' + variantName, 10); -}); - - -ExperimentWrapper = connect( - state => { - return { - uid: state.offchain.get('uid') - } - } -)(ExperimentWrapper); - -ExperimentWrapper.win = (name) => emitter.emitWin(name); - -export default ExperimentWrapper; diff --git a/src/app/components/elements/LocalizedCurrency.jsx b/src/app/components/elements/LocalizedCurrency.jsx deleted file mode 100644 index d4085d8d8..000000000 --- a/src/app/components/elements/LocalizedCurrency.jsx +++ /dev/null @@ -1,6 +0,0 @@ -import { FRACTION_DIGITS, DEFAULT_CURRENCY } from 'app/client_config'; - -let localCurrencySymbol = DEFAULT_CURRENCY; -let localizedCurrency = (value) => value; - -export { localizedCurrency, localCurrencySymbol } diff --git a/src/app/components/elements/SaveLogin.jsx b/src/app/components/elements/SaveLogin.jsx deleted file mode 100644 index 7de733b7d..000000000 --- a/src/app/components/elements/SaveLogin.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import Tooltip from 'app/components/elements/Tooltip' -import {connect} from 'react-redux' -import user from 'app/redux/User' -import tt from 'counterpart'; - -const {bool, func} = PropTypes - -class SaveLogin extends Component { - static propTypes = { - saveLoginConfirm: bool, - yes: func, - no: func, - } - render() { - const {props: {saveLoginConfirm, no, yes}} = this - if (!saveLoginConfirm) return - setTimeout(() => {no()}, 7.5 * 1000) - return ( - - - {tt('g.auto_login_question_mark')} {tt('g.yes')} / {tt('g.no')} - - - ) - } -} -// export default connect( -// state => { -// if (!state.user) return -// return { -// saveLoginConfirm: state.user.get('saveLoginConfirm'), -// } -// }, -// dispatch => ({ -// no: () => {dispatch(user.actions.saveLoginConfirm(false))}, -// yes: () => { -// dispatch(user.actions.saveLoginConfirm(false)) -// dispatch(user.actions.saveLogin()) -// }, -// }) -// )(SaveLogin) diff --git a/src/app/components/elements/SignupProgressBar.jsx b/src/app/components/elements/SignupProgressBar.jsx deleted file mode 100644 index 0927728ae..000000000 --- a/src/app/components/elements/SignupProgressBar.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -export default function SignupProgressBar({steps, current}) { - const lis = steps.map((s, i) => { - const cn = i + 1 < current ? 'done' : (i + 1 == current ? 'current' : ''); - return
  • {s}
  • - }); - return
    -
    -
    -
      - {lis} -
    -
    -
    -
    ; -} diff --git a/src/app/components/elements/SignupProgressBar.scss b/src/app/components/elements/SignupProgressBar.scss deleted file mode 100644 index f5c1427ea..000000000 --- a/src/app/components/elements/SignupProgressBar.scss +++ /dev/null @@ -1,73 +0,0 @@ -.SignupProgressBar__container { - background-color: transparent; - padding-bottom: 0.5rem; - border-bottom: 1px solid $light-gray; -} - -.SignupProgressBar { - width: 100%; - margin: 0 auto 50px 0; - - > ul { - counter-reset: step; - } - > ul > li { - list-style-type: none; - width: 33%; - float: left; - font-size: 12px; - position: relative; - text-align: center; - text-transform: uppercase; - color: #7d7d7d; - } - > ul > li:before { - width: 30px; - height: 30px; - content: counter(step); - counter-increment: step; - line-height: 26px; - border: 2px solid #7d7d7d; - display: block; - text-align: center; - margin: 0 auto 10px auto; - border-radius: 50%; - background-color: white; - } - > ul > li:after { - width: 100%; - height: 2px; - content: ''; - position: absolute; - background-color: #7d7d7d; - top: 15px; - left: -50%; - z-index: -1; - } - > ul > li:first-child:after { - content: none; - } - > ul > li.done { - color: #1A5099; - } - > ul > li.done:before { - content: "\2713"; - color: $white; - border-color: #1A5099; - background-color: #1A5099; - } - > ul > li.done:after { - background-color: #1A5099; - } - > ul > li.current { - color: #1A5099; - } - > ul > li.current:before { - color: $white; - border-color: $color-teal; - background-color: $color-teal; - } - > ul > li.current:after { - background-color: #1A5099; - } -} diff --git a/src/app/components/elements/Template.jsx b/src/app/components/elements/Template.jsx deleted file mode 100644 index d4c05d546..000000000 --- a/src/app/components/elements/Template.jsx +++ /dev/null @@ -1,72 +0,0 @@ -/* eslint react/prop-types: 0 */ -import React from 'react' -// import g from 'app/redux/GlobalReducer' -// import transaction from 'app/redux/Transaction' -import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' -// import {Map} from 'immutable' - -// const {string, object} = React.PropTypes - -class Template extends React.Component { - // static propTypes = { - // } - - // static defaultProps = { - // } - - // constructor() { - // super() - // this.state = {} - // } - - - componentWillMount() { - } - - componentDidMount() { - } - - // componentWillReceiveProps(nextProps) { - // } - - // This is based on react PureRenderMixin, it makes the component very efficient by not re-rendering unless something in the props or state changed.. PureRenderMixin comes highly recommended. shouldComponentUpdate adds a debug boolean to show you why your component rendered (what changed, in the browser console type: steemDebug_shouldComponentUpdate=true). - shouldComponentUpdate = shouldComponentUpdate(this, 'ReplyEditor') - - // componentWillUpdate(nextProps, nextState) { - // // Can't call this.setState() here, use componentWillReceiveProps instead - // } - - // componentDidUpdate(prevProps, prevState) { - // } - - componentWillUnmount() { - } - - render() { - const {} = this.props - return ( - - - ) - } -} - -import {connect} from 'react-redux' - -export default connect( - (state, ownProps) => { - // const username = state.user.getIn(['current', 'username']) - return { - ...ownProps, - // username, - } - }, - // dispatch => ({ - // dispatchAction: (abc) => { - // dispatch({ - // type: 'user/TYPE', - // payload: {abc}, - // }) - // }, - // }) -)(Template) diff --git a/src/app/components/pages/Post.jsx b/src/app/components/pages/Post.jsx index d0a450dff..c0a6e40de 100644 --- a/src/app/components/pages/Post.jsx +++ b/src/app/components/pages/Post.jsx @@ -9,7 +9,6 @@ import {sortComments} from 'app/components/cards/Comment'; import FoundationDropdownMenu from 'app/components/elements/FoundationDropdownMenu'; import {Set} from 'immutable' import tt from 'counterpart'; -import { localizedCurrency } from 'app/components/elements/LocalizedCurrency'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; import { INVEST_TOKEN_UPPERCASE } from 'app/client_config'; @@ -167,7 +166,7 @@ class Post extends React.Component {
    {tt('g.next_7_strings_single_block.authors_get_paid_when_people_like_you_upvote_their_post')}. -
    {tt('g.next_7_strings_single_block.if_you_enjoyed_what_you_read_earn_amount', {amount: '$'+localizedCurrency(signup_bonus.substring(1)), INVEST_TOKEN_UPPERCASE})} +
    {tt('g.next_7_strings_single_block.if_you_enjoyed_what_you_read_earn_amount', {amount: '$'+signup_bonus.substring(1), INVEST_TOKEN_UPPERCASE})}
    -- GitLab From e5d4aaeb757272b2e2fc2ed0f591e95ca2eff9c4 Mon Sep 17 00:00:00 2001 From: TimCliff Date: Wed, 29 Nov 2017 20:01:26 -0600 Subject: [PATCH 080/399] Update Manual Build Instructions - node 7.5 to 8.7 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index aca00eedc..8fbdfc1cf 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,13 @@ mkdir tmp #### Install dependencies -Install at least Node v7.5 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 7.5, npm 4.1.2, and yarn 1.1.0. +Condenser is known to successfully build using node 8.7, npm 4.1.2, and yarn 1.1.0. Using nvm, you would install like this: ```bash -nvm install v7.5 +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. -- GitLab From 5a6d8163441b7c6c45d9e853f1563293a82e4669 Mon Sep 17 00:00:00 2001 From: TimCliff Date: Wed, 29 Nov 2017 20:40:12 -0600 Subject: [PATCH 081/399] Update faq.md - change tag names to ids --- src/app/help/en/faq.md | 340 ++++++++++++++++++++--------------------- 1 file changed, 170 insertions(+), 170 deletions(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index d4f0f55ae..78c45b31d 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1,8 +1,8 @@ # Steemit FAQ -## Table of Contents +## Table of Contents -### General +### General - What is Steemit.com? - How does Steemit work? - How does Steemit differ from other social media websites? @@ -12,7 +12,7 @@ - Where does the value come from? - Why are people getting vastly different rewards? -### Accounts +### Accounts - How do I create an account? - What information do I need to provide in order to create an account? - How long does the account approval process take? @@ -24,11 +24,11 @@ - Am I allowed to create more than one account - Can I delete or deactivate my account? -### Community +### Community - Is there an Etiquette Guide for Steemit? - Am I required to verify my identity? -### Site Navigation +### Site Navigation - How do I upvote a post or comment? - What do the Home, New, Hot, Trending, and Promoted links show? - What information is available in my account menu? @@ -47,7 +47,7 @@ - Can I see the list of users I am following, and who is following me? - What languages are supported? -### Posting +### Posting - What can users post to Steemit? - What are the different choices for post rewards (50%/50%, Power Up 100%, Decline Payout)? - How do I add images and photos to my posts? @@ -67,11 +67,11 @@ - What does "Promoting" a post do? - How do I promote a post? -### Comments +### Comments - Can I earn digital tokens for commenting? - How often can I comment? -### Economics +### Economics - Where do the new STEEM tokens come from? - How many new tokens are generated by the blockchain? - How are the new tokens distributed? @@ -100,7 +100,7 @@ - Are there fees for Powering Up, Powering Down, trading on the internal market, or converting SBD to STEEM? - How long does it take to transfer STEEM or SBD tokens between users? -### Voting and Curating +### Voting and Curating - What is my voting power? - How many times can I vote without depleting my voting power? - Can I vote with less than 100% of my voting strength? @@ -116,7 +116,7 @@ - Will a downvote hurt my reputation? - What is the difference between a downvote and a flag? -### Plagiarism, Spam, and Abuse +### Plagiarism, Spam, and Abuse - What is considered spam or abuse? - What are Steemit’s policies on plagiarism? - Is it okay to use random pictures from the internet? @@ -124,18 +124,18 @@ - What is @cheetah? - Where do I report a post or comment that contains plagiarism, spam, or abuse? -### Reputation +### Reputation - What is Reputation? - How is the Reputation score measured? - How do I improve my reputation score? - What causes my reputation score to go down? - Why does my reputation score matter? -### Followers, Feeds, and Resteem +### Followers, Feeds, and Resteem - What is Resteeming? - Can I share on other social media? -### Blockchain +### Blockchain - What is a blockchain? - What is the Steem blockchain? - What is the difference between Steem and Steemit? @@ -146,7 +146,7 @@ - Where can I find the information for the official launch of the blockchain? - Can I mine STEEM? -### Steemit, Inc. +### Steemit, Inc. - Who is the CEO of Steemit? - Can I invest in Steemit? - What does Steemit’s development roadmap look like? @@ -155,7 +155,7 @@ - Did Steemit "pre-mine" tokens? - What is the Steemit Privacy Policy? -### Security +### Security - How can I keep my Steem account secure? - Why should I be careful with my master password? - Why is the master password a long string of gibberish? @@ -166,31 +166,31 @@ - How does the stolen account recovery process work? - How do I report a security vulnerability? -### Developers +### Developers - Are the Steem blockchain and Steemit.com code open-source? - Is there a Github page for Steemit.com? - Is there a Github page for the Steem blockchain? - What is available for developers interested in Steem and Steemit? - How do I use cli_wallet? -### Witnesses +### Witnesses - What are Steem witnesses? - How can I vote for witnesses? - How many witnesses can I vote for? -### Miscellaneous +### Miscellaneous - What third-party tools are there for Steemit? - Is there an official Steemit Facebook page? - Is there an official Steemit Twitter account? - What is the Steem Whitepaper and what is its purpose? - Where can I ask for help if my question was not answered here? -### Disclaimer +### Disclaimer - Third Party References and User Links # General - + ## What is Steemit.com? Steemit is a social network and content rewards platform that makes the crowd the beneficiaries of the attention economy. It does this be rewarding users with STEEM. @@ -198,25 +198,25 @@ Steemit is a social network and content rewards platform that makes the crowd th Steemit has redefined social media by building a living, breathing, and growing social economy; a community where users are getting rewarded for sharing their voice. ^ - + ## How does Steemit work? Steemit is a social media platform that works by having the crowd reward the crowd for their content. It does this thanks to the Steem blockchain and cryptocurrency; Steem is 'minted' daily and distributed to content producers according to the votes they get. ^ - + ## How does Steemit differ from other social media websites? Most social media sites extract value from their userbase for the benefit of shareholders alone. Steemit is different, it's a new kind of attention economy. By connecting with the Steem blockchain (which is decentralized and controlled by the crowd), Steemit users receive all the benefits and rewards for their attention. ^ - + ## Does it cost anything to post, comment, or vote? No. It is free to post, comment, and vote on content on Steemit.com. You might even get paid for it! ^ - + ## Can I earn digital tokens on Steemit? How? You can earn digital tokens on Steemit by: @@ -228,13 +228,13 @@ You can earn digital tokens on Steemit by: **Purchasing** - Users can purchase STEEM or Steem Dollar tokens directly through the Steemit wallet using bitcoin, Ether, or BitShares tokens. They are also available from other markets and exchanges including BlockTrades, Poloniex, Bittrex, Shapeshift.io, and Changelly. STEEM tokens that are powered up to STEEM Power earn a small amount of interest for holding. ^ - + ## Where do the tokens come from? The Steem network continually creates digital tokens to reward content creators and curators. Some of the newly-created tokens are transferred to users who add value to Steemit by posting, commenting, and voting on other people's posts. The remainder is distributed to holders of STEEM Power and the witnesses that power the blockchain. ^ - + ## Where does the value come from? At its root, Steem is simply a points system. However, because this points system is blockchain-based, the points can be traded on markets as tokens. People buy and sell these tokens, and many hold in anticipation of increased purchasing power for various Steem-related services. @@ -242,7 +242,7 @@ At its root, Steem is simply a points system. However, because this points syste By analogy, Steem is a game system where users compete for attention and rewards by bringing content and adding value to the platform. The rewards people earn are tokens that have market value and are readily tradable. It is similar to how someone playing a video game could obtain a limited item or currency by playing the game. If the currency or items are transferable between users, then they can sell or buy them on game item markets. ^ - + ## Why are people getting vastly different rewards? Steemit is not a "get rich quick" scheme. While it is possible to post content that goes viral quickly and earn a lot of rewards on a single post, this is not typical for most users. @@ -254,7 +254,7 @@ It is best to have realistic expectations, without focusing on rewards when you ^ # Accounts - + ## How do I create an account? Click on the "Sign Up" link at the top of Steemit.com to get started. @@ -264,13 +264,13 @@ You will be asked to verify your email address and phone number. After your emai After you receive notification that your account is approved, click on the link in the email to finish the account creation process. Be sure to save and backup your username and password. It is very important that you do not lose your password. There is no way to recover your password or access your account if it is lost. Once your password is saved and backed up, click on the "Create Account" button to create the account. ^ - + ## What information do I need to provide in order to create an account? You will need to provide your email address and phone number. ^ - + ## How long does the account approval process take? Most accounts are approved within 24 hours. Some may take up to a week. @@ -278,19 +278,19 @@ Most accounts are approved within 24 hours. Some may take up to a week. If your account has not been approved after one week, please ask for help in the #help channel on steemit.chat. ^ - + ## Why do I need to provide my email and phone number? To create an account on the blockchain, it costs STEEM tokens. When you create an account through Steemit.com, Steemit Inc. is supplying the tokens to pay the account creation fee. In order to prevent users from abusing the paid-for signup and creating multiple accounts, we need to be able to verify that each user is only signing up for one account. ^ - + ## Can I create a Steem account without an email and phone number? The only way to have an account created via Steemit.com is to supply your email and phone number. Because Steem is an open and permissionless network, there are other ways to create a Steem account. Any Steem blockchain account can be used on Steemit.com ^ - + ## What are other ways to create an account on the blockchain besides using Steemit.com? If you are willing to pay your own signup fee, then there are other ways to create a new account on the blockchain. @@ -300,25 +300,25 @@ There is a third-party tool called SteemConnect that allows you to create accounts by paying or delegating the account creation fee. There is no additional fee to use the service, but does require an existing Steem blockchain account to pay the account creation fee to create the account. ^ - + ## It is not letting me create an account with my phone number. What should I do? Ask for help in the #help channel on steemit.chat. ^ - + ## What happens if my email or phone number changes? Currently there is no way to change the email or phone number that is linked to your account. Though once your account is created, you can continue to use it even if the email or phone number that is linked to the account has changed. ^ - + ## Am I allowed to create more than one account? Each user is allowed only one paid-for account created via Steemit.com, however users are allowed to create multiple accounts on the blockchain. Creating additional accounts on the blockchain requires users to pay their own account creation fee for any additional accounts. ^ - + ## Can I delete or deactivate my account? Accounts can not be deactivated or deleted. The account along with all of its activity is permanently stored in the blockchain. @@ -326,13 +326,13 @@ Accounts can not be deactivated or deleted. The account along with all of its ac ^ # Community - + ## Is there an Etiquette Guide for Steemit? There are no official rules for participating on Steemit.com, but one of the users @thecryptofiend has created an Etiquette Guide for the community. While it is not required to follow the suggestions in the guide, they are standards that many users in the community choose to follow. ^ - + ## Am I required to verify my identity? Verification is a process where users give evidence to show that they are the person that they claim to be. This is to reduce fraud and people impersonating known figures. If you would like to remain anonymous, that is perfectly fine. However if you claim to be someone specific, the community may expect that you verify you are who you say you are. @@ -344,13 +344,13 @@ Many users also like to post a photo or a video which shows them holding up a sh ^ # Site Navigation - + ## How do I upvote a post or comment? To upvote a post or comment, click on the "upvote" icon at the bottom of the post/comment. ^ - + ## What do the Home, New, Hot, Trending, and Promoted links show? These are various ways to sort Steem posts. @@ -366,7 +366,7 @@ These are various ways to sort Steem posts. **Promoted** - Listings that are boosted by Steem Dollar payments get promoted for greater visibility. ^ - + ## What information is available in my account menu? You can get to your account menu by clicking on the avatar icon in the top-right corner of a Steemit.com page. @@ -388,7 +388,7 @@ You can get to your account menu by clicking on the avatar icon in the top-right **Logout** - If you'd like to logout. ^ - + ## How do I see my recent rewards? The Rewards drop-down menu is available on your profile/blog page. Click it and there are two links: @@ -400,19 +400,19 @@ The Rewards drop-down menu is available on your profile/blog page. Click it and You can also view the same information for other users by visiting their profile. ^ - + ## What information is shown in my wallet? Your wallet shows how many STEEM and Steem Dollar tokens you have in your account. It shows how much STEEM Power it has, and how much SP is delegated. It also shows how many of your STEEM and Steem Dollar tokens are being held in the savings account, which is a balance that is subject to 3 day withdraw waiting period. The wallet page shows any the progress of any Steem Dollar to STEEM conversions as well as the status of a power down. It also shows an estimated value of all the tokens in your account, based on the recent market prices of STEEM and SBD. ^ - + ## How do I transfer my STEEM or Steem Dollars into savings? Your savings balance is STEEM and SBD tokens that are subject to 3 day withdraw waiting period. This is an extra security measure in case your account credentials are compromised. To transfer STEEM or SBD tokens into savings, click on the drop-down arrow next to STEEM or STEEM DOLLARS in your wallet, and select "Transfer to Savings". ^ - + ## How do I send money to another user? - From your wallet page, click the STEEM or Steem Dollar balances with the down arrow next to them. @@ -424,7 +424,7 @@ Your savings balance is STEEM and SBD tokens that are subject to 3 day withdraw - You will be prompted for your password. You will need to enter your master password or active key. ^ - + ## Will I receive notifications when there is activity with my account? When there is new activity in your feed, you receive a reply from another user, or there is a new transfer in your wallet, you will receive a notification in your account menu. It will show a little red number showing the number of new notifications. @@ -434,7 +434,7 @@ Steemit also allows you to subscribe to receive additional notifications when us Currently, there are no options to receive notifications for votes directly on Steemit.com. But, there is a third-party application https://steemstats.com/, developed by @jesta, which has an option to set up additional notifications on your computer. ^ - + ## What is shown in my profile? At the top of your profile is your display name and reputation score. Below your display name is the number of followers you have, the number of posts and comments you have written, and the number of people you are following. It also shows the month and year when your account was created. @@ -444,19 +444,19 @@ You have the option to change your avatar and display name on the Settings page. You can view your own profile by clicking on the link to your Blog in your account menu. ^ - + ## How do I change my avatar image and other profile information? Your profile info, avatar image, and cover image are set in your Settings page. In order to update your avatar picture and cover image, you will need to host the images somewhere. This can be done by uploading it to a Steemit comment or post, or using a third-party image host such as Postimage. Once your image is uploaded, copy its URL and paste it into the "Profile Picture URL" box for the avatar, or the "Cover Image URL" box for the cover image. Then click the Update button and enter your password or active key. ^ - + ## What is the recommend size for the cover image? The cover image will be resized/scaled depending on the device being used. Therefore it is recommend to use an image that will still look good when cropped or resized. A 2048x512 image is the optimal size to work for most devices. ^ - + ## How can I control whether I see "Not Safe For Work" (NSFW) content? By default, content that users have tagged as "NSFW" will be hidden, but a link will be shown to reveal the content. @@ -464,7 +464,7 @@ By default, content that users have tagged as "NSFW" will be hidden, but a link You can update your display preference with the Settings page so that NSFW content is always shown by default, remains hidden until clicked, or is completely hidden with no option to reveal. ^ - + ## How do I search for content? In the upper right corner of Steemit, there is a magnifying glass search link where you can find posts using a keyword search. @@ -472,25 +472,25 @@ In the upper right corner of Steemit, there is a magnifying glass search link wh There is also an **Explore** link in the main menu, where you can browse through posts based on tags. ^ - + ## Can I see which users I have muted? Yes. This can be seen under the Settings page. ^ - + ## Can I see which users have muted me? No. This information is not presented on Steemit.com. ^ - + ## Can I see the list of users I am following, and who is following me? Yes. You can see the list of followers or people you are following by clicking on the links on your profile page. ^ - + ## What languages are supported? English is the most-used language used on the Steemit platform, but communities are forming that speak other languages. @@ -498,13 +498,13 @@ English is the most-used language used on the Steemit platform, but communities ^ # Posting - + ## What can users post to Steemit? Steem is an open platform meant to host and welcome any legal content. Users can post anything they want, whether it be phrases, quotes, blogs, anecdotes, photos, videos, memes, songs, and more. Be creative! ^ - + ## What are the different choices for post rewards (50%/50%, Power Up 100%, Decline Payout)? - **50%/50%** - This rewards in half STEEM Power, and half liquid STEEM / Steem Dollars. The ratio of liquid STEEM to Steem Dollars rewarded is based on network conditions at the time of payout. This is the default payout option. @@ -514,7 +514,7 @@ Steem is an open platform meant to host and welcome any legal content. Users can - **Decline Payout** - Use this option to receive no post rewards. Votes will affect the post's position on the trending ranking but no rewards are paid from Steem's reward pool. Replies made to the post are still eligible for rewards. ^ - + ## How do I add images and photos to my posts? You can browse your hard drive to add an image by clicking on the "selecting them" link from within the editor. @@ -524,19 +524,19 @@ If you have an image copied to your clipboard, you can simply paste (`ctrl + v`) Pictures can also be hosted on an external site. Paste the image's web address (URL) into the editor and it will automatically be added. ^ - + ## How do I set the thumbnail image for my post? The first image in the post will automatically be set as the thumbnail image. ^ - + ## What is the recommend aspect ratio for thumbnail images? The recommend aspect ratio for thumbnail images is 16x9. ^ - + ## How do I add videos to my posts? To add a YouTube or Vimeo video to your blog post, simply paste the link to the video into the post. @@ -544,19 +544,19 @@ To add a YouTube or Vimeo video to your blog post, simply paste the link to the You can also read this guide from @algimantas, which has more detailed instructions: ^ - + ## Is there a way I can make my images smaller? Yes, but the picture must be resized before it is uploaded into the Steemit.com editor. This can be done in your favorite photo editing software, or online by uploading to a third-party website that features editing such as imgur.com. ^ - + ## What are tags? Tags are a way to categorize your content, so that others can find it. The more relevant the tags are to the post, the more like-minded people will come across it. ^ - + ## What tags should I use? Try to use tags that are relevant to your post, and that will be popular for other people to browse. For example, "mytriptoalaska" may be relevant to your post, but readers are probably not going to go searching for that. Using "travel" would be a better choice for a tag in this case. @@ -568,19 +568,19 @@ Be mindful when choosing tags. If your tags aren’t related to your post, your All tags must be lowercase letters. Spaces aren't allowed, but hyphenated words with a single dash are. ^ - + ## How many tags can I use? You can use up to 5 tags per post. ^ - + ## Why is the "Post" button grayed out? A post must have a title, body, and at least one valid tag. If any of these are missing, then the "Post" button will be disabled. ^ - + ## How do I format text in Markdown? Some common markdown syntax is: @@ -603,19 +603,19 @@ Text can be sized using headers: For more advanced formatting, a guide describing the common markdown formatting syntax can be found here: Markdown Cheatsheet ^ - + ## How often can I post? You are allowed to post almost as often as you like. Currently, posts must be spaced 5 minutes apart. However, the community may not find value in users that are posting too frequently. Keep in mind what your audience will be interested in viewing, so that you do not overwhelm your followers with too much content. ^ - + ## How long can my post be? Post sizes are limited to about 64,000 characters including formatting. This is ample for most posts. If writing blogs, consider how much people are willing to read at one time. If you make your posts too long, readers may lose interest which may affect the amount of upvotes and rewards you receive. ^ - + ## If posting in a language other than English, how will I get recognized? You can use language-specific tags to help you to reach the audience that speaks your language. @@ -630,13 +630,13 @@ Language-specific groups include: - Portuguese = pt ^ - + ## Can I delete something I posted? The blockchain will always contain the full edit history of posts and comments, so it can never be completely deleted. If you would like to update a post so that users cannot see the content via Steemit.com, you can edit the post and replace it with blank content for as long as the post is active. After seven days, the post can no longer be edited. ^ - + ## What does "Promoting" a post do? When you make a post, there is the option to promote it with Steem Dollars. It will then show up in the “Promoted” tab. The order that it appears in the list depends on how much the post was promoted for. Posts with a higher promoted amount will be higher than posts with less. @@ -646,7 +646,7 @@ Steem Dollars spent to promote a post are paid to the account @null, which nobod You can promote your own posts, or posts that you like from other users. ^ - + ## How do I promote a post? At the bottom of each post is a button to "Promote". After clicking the button, type the number of Steem Dollars that you want to spend and click “PROMOTE”. The operation will require your master password or active key. @@ -654,13 +654,13 @@ At the bottom of each post is a button to "Promote". After clicking the button, ^ # Comments - + ## Can I earn digital tokens for commenting? Yes, comments that are upvoted can earn rewards just like posts! ^ - + ## How often can I comment? There is a 20 second wait time in between comments to limit spam. @@ -668,19 +668,19 @@ There is a 20 second wait time in between comments to limit spam. ^ # Economics - + ## Where do the new STEEM tokens come from? Blockchains like Steem and Bitcoin produce new tokens each time a block is produced. Unlike Bitcoin, where all of the new coins go to the block producers (called miners), the Steem blockchain allocates a majority of the new tokens to a reward fund. The reward fund gives users tokens for participating in the platform. ^ - + ## How many new tokens are generated by the blockchain? Starting with the network's 16th hard fork in December 2016, Steem began creating new tokens at a yearly inflation rate of 9.5%. The inflation rate decreases at a rate of 0.01% every 250,000 blocks, or about 0.5% per year. The inflation will continue decreasing at this pace until the overall inflation rate reaches 0.95%. This will take about 20.5 years from the time hard fork 16 went into effect. ^ - + ## How are the new tokens distributed? Out of the new tokens that are generated: @@ -689,19 +689,19 @@ Out of the new tokens that are generated: - The remaining 10% pays for the witnesses to power the blockchain. ^ - + ## What is the reward pool? Every day, a fixed amount of STEEM tokens are allocated to the network reward fund, commonly called the "reward pool." These get distributed to authors and curators for posting and voting on content. ^ - + ## How is the reward pool split between authors and curators? Up to 25% of a post's payout is awarded to curators (the people who upvoted the post) as a reward for discovering the content. The other 75% is awarded to the author. If curators vote for a post within the first 30 minutes of it being created, a portion of their curation reward is added to the author payout. This portion is linear to the age of the post between 0 and 30 minutes. Therefore upvoting at 15 minutes old will donate half of your potential curation reward to the author. ^ - + ## Will the reward pool pay out more or less depending on who votes? There is a fixed amount of STEEM coins that gets added to the rewards pool each day. In the short term, the amount of coins that get paid out may be higher or lower depending on the amount of voting activity, but over time it will pay out the full amount of rewards regardless of who votes. @@ -709,7 +709,7 @@ There is a fixed amount of STEEM coins that gets added to the rewards pool each Votes in Steem are stake-weighted. Therefore voters with more STEEM Power have a greater influence over the allocation than voters with less SP, but their votes do not increase the amount of rewards. ^ - + ## Why do the earnings for my post go up or down? The amount that is shown next to a post is a "**Potential Payout**". This is an estimated value of how much money the post will make based on the votes that have occurred so far. Depending on various factors, this value can go up or down until the payout window closes: @@ -724,13 +724,13 @@ The amount that is shown next to a post is a "**Potential Payout**". This is an - If the price of STEEM goes down, the potential payout of all posts can go down. ^ - + ## When can I claim my rewards? Posts and comments remain active for 7 days. When the period is over, you are able to claim their earned rewards. In your Wallet, click the Claim Rewards button to add the tokens to your account. ^ - + ## What is the difference between STEEM, STEEM Power, and Steem Dollars? **STEEM** - STEEM is the base liquid currency token in the platform. STEEM can be powered up into STEEM Power, traded for Steem Dollars, and transferred to other accounts. It is a cryptocurrency token, similar to bitcoin. @@ -740,7 +740,7 @@ Posts and comments remain active for 7 days. When the period is over, you are ab **Steem Dollars** - Steem Dollars (commonly abbreviated SBD) are liquid stable-value currency tokens designed to be pegged to $1 USD. Steem Dollars can be traded with STEEM, and transferred to other accounts for commerce or exchange. Steem Dollars may also be converted into STEEM in a process that takes 3.5 days. Steem Dollars can be used to buy things in marketplaces, such as PeerHub.com. ^ - + ## What is delegated STEEM Power? Users have the option to delegate STEEM Power to other users. When a user is delegated STEEM Power, their content votes and curation rewards are calculated as if it were their own STEEM Power. Users are not able to power down or cash out delegated STEEM Power, as it still belongs to the original owner. @@ -750,13 +750,13 @@ Most users will have a small amount of STEEM Power delegated to them by the Stee Delegated STEEM Power shows up in a user's wallet below their actual STEEM Power balance in parentheses. ^ - + ## What determines the price of STEEM? The price of STEEM is based on the supply and demand of the token, determined by buyers and sellers on the exchanges. It is similar to how the price of a commodity like gold is determined. ^ - + ## How do I get more STEEM Power? With STEEM tokens in your wallet, click "Power Up" to turn them into STEEM Power. If you have Steem Dollars, you can convert them to STEEM from your wallet, and then power up the STEEM. @@ -775,7 +775,7 @@ STEEM purchases made via Steemit.com are facilitated by Poloniex, Bittrex, ShapeShift.io, and Changelly. ^ - + ## How long does it take STEEM or STEEM Power that I purchased to show up in my account? Transactions on the Steem blockchain typically only take about three seconds to process, but when you are purchasing the STEEM tokens using bitcoin or some other token, then the transaction must wait for the transaction to be confirmed on the other network. This can often take several hours, and sometimes even days. @@ -783,7 +783,7 @@ Transactions on the Steem blockchain typically only take about three seconds to If you paid using bitcoin, the third party website bitcoinfees.21.co can estimate the approximate wait time of the transaction based on the fees that were paid. The third party website blockchain.info will lookup the fees that were paid on a specific blockchain transaction. ^ - + ## What is powering up and down? **Powering up** - If you have STEEM tokens, you can Power Up to STEEM Power to get more voting influence on posts and comments. Having more STEEM Power also increases the amount of curation rewards and interest that you can earn. More SP also grants more influence on approving Steem witnesses. @@ -791,7 +791,7 @@ If you paid using bitcoin, the third party website ^ - + ## What do the dollar amounts for pending payouts represent? The dollar amounts next to posts and comments are estimates of the potential payout that will occur when the payout period ends, based on the current voting activity and price of STEEM. These potential payout amounts may fluctuate up or down until the payout period ends. @@ -801,19 +801,19 @@ Payouts occur as a combination of STEEM Power and Steem Dollars. Sometimes the b The blockchain estimates the dollar value of STEEM and STEEM Power based on the 3.5 day average price of STEEM reported by the witnesses. The blockchain assumes Steem Dollars are worth approximately one USD. ^ - + ## Will 1 Steem Dollar always be worth $1.00 USD? The market value of a Steem Dollar is dictated by the supply and demand of the token. Therefore it is possible for 1 SBD to be worth more or less than 1 USD depending on market conditions. However, the network's SBD conversion feature serves as a mechanism to hold Steem Dollars within a small margin of the value of USD. ^ - + ## How do Steem Dollar to STEEM conversions work? If you convert Steem Dollars to STEEM on the Wallet page, the blockchain will process the transaction over a period of 3.5 days. At the end of the 3.5 days, the SBD will be gone and replaced by approximately $1 USD worth of STEEM tokens. The "approximately 1 USD worth of STEEM tokens" is based on the median STEEM price over the 3.5 days, using the price feeds from the Steem witnesses. Depending on price fluctuations during the 3.5 days it is possible to end up with more or less than $1 USD worth of STEEM per SBD at the end of the conversion. ^ - + ## Is there a way for me to convert my Steem Dollars to STEEM without waiting 3.5 days? You can exchange them. Visit the internal Market, found in the main menu. There you can exchange your SBD for STEEM in real-time at whatever the current market price is. @@ -821,7 +821,7 @@ You can exchange them. Visit the internal Market, found in the main menu. There Depending on market conditions, users may get more STEEM for their SBD by trading them for STEEM on the internal market, rather than using the conversion. ^ - + ## What can I do with my STEEM tokens? - "Power Up" to STEEM Power @@ -830,7 +830,7 @@ Depending on market conditions, users may get more STEEM for their SBD by tradin - Purchase items through third-party stores that accept STEEM tokens ^ - + ## What can I do with my SBD tokens? - Hold them as a stable-value token @@ -840,19 +840,19 @@ Depending on market conditions, users may get more STEEM for their SBD by tradin - Purchase items through third-party stores that accept SBD tokens ^ - + ## What is a MVEST? A VEST is a unit of measurement for STEEM Power. A MVEST is one million VESTS. The amount of STEEM Power in one MVEST can be found on steemd.com as `steem_per_mvests`. ^ - + ## Can I sell goods and services on Steemit? Other than making a post and making sales manually, there is no interface for selling items directly on Steemit.com. You can list goods on the third-party website PeerHub.com. Through PeerHub, you can accept payment in Steem Dollars or STEEM, and you have the option to advertise your items through Steemit posts. ^ - + ## How can I withdraw my STEEM or SBD coins? STEEM and SBD tokens are readily tradable to bitcoin, which is readily tradable to the local currency of your choice. There is a link to "Sell" your STEEM and SBD tokens in your wallet, which uses the BlockTrades interface. @@ -874,25 +874,25 @@ https://steemit.com/tutorial/@beanz/how-to-get-my-usdteemit-money-into-my-bank-a https://steemit.com/steemit/@shapeshiftio/official-announcement-shapeshift-has-added-steem-to-the-exchange ^ - + ## Will I get a 1099 from Steemit? No, you are not being paid by Steemit. The Steem network rewards you. It is your responsibility to determine what, if any, taxes apply to the transactions you make. Further, it is your responsibility to report and remit the correct tax to the appropriate tax authority. By creating an account, you agree that Steemit Inc is not responsible for determining whether taxes apply to your Steem transactions or for collecting, reporting, withholding, or remitting any taxes arising from any Steem transactions. ^ - + ## How much are the transaction fees for sending tokens to other users? There are never any fees for transfers within the Steem network. However, if you transfer Steem to an exchange and convert it to another currency, you will incur a small fee from the exchange. ^ - + ## Are there fees for Powering Up, Powering Down, trading on the internal market, or converting SBD to STEEM? No. None of these actions incur any fees. ^ - + ## How long does it take to transfer STEEM or SBD tokens between users? A transfer of tokens between accounts typically takes 3 seconds. This is far faster than most blockchain tokens. @@ -900,7 +900,7 @@ A transfer of tokens between accounts typically takes 3 seconds. This is far fas ^ # Voting and Curating - + ## What is my voting power? Voting power is like an "energy bar" in a computer game that goes down a little bit every time you vote. You start out with 100% voting power. Every time you vote, you will use a small amount of your voting power. @@ -908,13 +908,13 @@ Voting power is like an "energy bar" in a computer game that goes down a little As you use more of your voting power, your votes will carry less influence. A vote with 50% voting power left will be worth 1/2 as much as a vote cast with 100% voting power. Not to worry, the network recharges your voting power by 20% every day. ^ - + ## How many times can I vote without depleting my voting power? Every 100% vote you cast will use 2% of your remaining voting power. Your voting power will recharge by 20% each day. You can vote more than 10 times per day, but each vote will be worth less, and it will take longer to reach full voting power again. ^ - + ## Can I vote with less than 100% of my voting strength? New users can only upvote and downvote with 100% voting strength. @@ -926,13 +926,13 @@ Once you reach about 500 STEEM Power, you will see a vote slider appear when you Upvotes and downvotes use the same amount of voting power. ^ - + ## Where can I check my voting power? You can view your current voting power using third party tools such as https://steemd.com/@youraccount or https://steemstats.com. ^ - + ## What determines how much of the curation reward goes to the author versus curators? The rewards are allocated so that 75% of the payout goes to the author of the post/comment, and 25% goes to the curator. @@ -946,25 +946,25 @@ Of the 25% that goes to the curator, that portion will be split between the auth - If a post is upvoted 30 min after posting, 100% of the curation reward goes to the curator. ^ - + ## Can I get curation rewards for upvoting comments? Yes. You can earn curation rewards from upvoting both posts and comments! ^ - + ## Do I get curation rewards for downvoting posts or comments? No. Since downvoting reduces the rewards on a post/comment, it does not earn curation rewards. ^ - + ## What are curation trails? Some users decide to use third party applications such as Streemian to automatically cast votes. Users can automatically vote for the same posts and comments that other users does. Typically they will set this up to follow the votes of users who are good at curating. When a user has other users automatically voting for the same content that they do, the people that automatically vote after them are called their "curation trail". ^ - + ## Why don't my upvotes have an effect on a post's rewards? A user with more SP is going to have a larger influence on the rewards than users with less SP. One vote from a user with a lot of SP can often have more of an effect than 100 votes from users with a small amount of SP. @@ -972,7 +972,7 @@ A user with more SP is going to have a larger influence on the rewards than user Even though your vote may not have an immediate effect, when it gets added in along with all the other votes at the end of the payout period, it can still affect the payout. It may also cause more users to vote on the post too, because they saw that you upvoted it - so your votes can have an indirect effect on the payout this way. ^ - + ## Is there a way to make my votes count for more? Yes. The more STEEM Power you have, the more influence your votes will have. @@ -980,25 +980,25 @@ Yes. The more STEEM Power you have, the more influence your votes will have. The platform does not require that anybody purchase SP in order to participate, and there are many users who have earned a lot of STEEM Power without spending any of their own money. You have the option of purchasing more STEEM Power through your Steemit wallet. ^ - + ## What are the valid reasons for downvoting? Users are allowed to downvote for any reason that they want. There are many users in the community who recommend only using the downvote on posts that are abusive. It is up to you if you want to follow this etiquette. ^ - + ## Does a downvote mean that I did something wrong? Just because you received a downvote does not mean that you did something wrong. The downvoting person may have just been voting to reallocate the rewards in a way that they felt was more beneficial to the other active posts in the platform. Often users will leave a comment explaining why they downvoted, but sometimes they might not. If they left a reason, it is up to you to determine if you did anything wrong, and if there is anything you want to change. ^ - + ## Will a downvote hurt my reputation? Not necessarily. See: What causes my reputation score to go down? ^ - + ## What is the difference between a downvote and a flag? With the current implementation, there is no difference between a downvote and a flag. They are treated the same at the blockchain level. @@ -1006,7 +1006,7 @@ With the current implementation, there is no difference between a downvote and a ^ # Plagiarism, Spam, and Abuse - + ## What is considered spam or abuse? - Asking for money, views, upvotes, follows, or resteems. @@ -1024,13 +1024,13 @@ With the current implementation, there is no difference between a downvote and a - Scams or Fraudulent offers. ^ - + ## What are Steemit’s policies on plagiarism? If you are posting plagiarized or copied content, you can get in legal trouble for violating copyright laws. Plagiarized posts and spam are seen as abuse and will be downvoted by community members. If you are posting or using someone else’s content, you must ensure that you have the rights to use the content, and properly reference the sources where you got the material from. ^ - + ## Is it okay to use random pictures from the internet? If you are using an image that is not your own, make sure you are allowed to use the image, and cite the source of the image. @@ -1041,14 +1041,14 @@ Here is a post from @mindover that has links to many websites that have images y https://steemit.com/steem-help/@mindover/don-t-plagiarize-images-here-are-13-free-and-legal-ways-to-find-high-quality-photos-you-can-use-on-steemit ^ - + ## What is Steemcleaners? Steemcleaners are a group of Steemians concerned with plagiarism, copy/paste, spam, scams and other forms of abuse on Steemit. https://steemit.com/steemcleaners/@steemcleaners/announcing-steemcleaners-the-steemit-abuse-fighting-team ^ - + ## What is @cheetah? @cheetah is a bot developed by @anyx that scours Steemit for copy/pasted content. Cheetah will not downvote copied content, but it alerts other users to look into it further. @@ -1059,7 +1059,7 @@ More information on the @cheetah bot can be found in this post: https://steemit.com/steemit/@cheetah/faq-about-cheetah ^ - + ## Where do I report a post or comment that contains plagiarism, spam, or abuse? You can report any abusive content to the #steemitabuse channel on steemit.chat. @@ -1067,7 +1067,7 @@ You can report any abusive content to the ^ # Reputation - + ## What is Reputation? Every user has a reputation score next to their name. The reputation score is one way Steemit measures the amount of value you have brought to the community. It is also a mechanism that is designed to help reduce abuse of the Steemit platform. @@ -1077,7 +1077,7 @@ Your reputation goes up when accounts vote on your content. Getting downvoted by Users with a lower reputation score are unable to affect your reputation. ^ - + ## How is the Reputation score measured? Every new user starts off with a reputation score of 25. @@ -1088,13 +1088,13 @@ More information about the calculation of the reputation score can be found in t https://steemit.com/steemit/@digitalnotvir/how-reputation-scores-are-calculated-the-details-explained-with-simple-math ^ - + ## How do I improve my reputation score? Every time another user upvotes one of your posts or comments, it increases your reputation score. Users with a higher reputation than you will have more of a positive effect. The more STEEM Power that the voter has, the larger the effect is as well. The best way to earn upvotes is by adding value to the Steemit community. ^ - + ## What causes my reputation score to go down? The only way for your reputation score to go down is to be downvoted by another user. Not all downvotes will cause a reputation loss though. @@ -1103,7 +1103,7 @@ The only way for your reputation score to go down is to be downvoted by another - If your post or comment that was downvoted still received more upvotes than downvotes (weighted by SP), then the net effect on your reputation score will still be positive. ^ - + ## Why does my reputation score matter? A reputation score is one way Steemit measures the amount of value you have brought to the community. In real estate, they say there are three variables of the utmost importance: location, location, location. On Steemit, those things are: reputation, reputation, reputation. It’s not to say other variables aren’t important, but reputation will be an enormous factor in your level of success. @@ -1115,13 +1115,13 @@ It is worth noting that if your reputation score goes below 0, Steemit will hide ^ # Followers, Feeds, and Resteem - + ## What is Resteeming? This is like reblogging or sharing posts on other platforms. Once you resteem a post it will appear in your feed and in your followers' feeds as if you had posted it yourself. Use it conservatively and with caution. It is great to want to share content you like and appreciate with people you follow, but you don't want to overwhelm your followers either. ^ - + ## Can I share on other social media? Yes you can use the share button to share on Facebook, Twitter or LinkedIn. You are welcome to post your Steemit links on other websites and social media sites. @@ -1129,19 +1129,19 @@ Yes you can use the share button to share on Facebook, Twitter or LinkedIn. You ^ # Blockchain - + ## What is a blockchain? A blockchain is a public ledger of all transactions ever executed. All of the transactions and data are stored in a distributed database. Each time the database is updated, all of updates are done together in a batch called a 'block'. Each time a new block is produced/added, it is appended on to all of the previous blocks - hence the name "blockchain". ^ - + ## What is the Steem blockchain? The Steem blockchain is the publicly accessible distributed database, which records all posts and votes, and distributes the rewards across the network. It is where all of the text content and voting data is stored, and it is where all of the reward calculations and payouts are performed. ^ - + ## What is the difference between Steem and Steemit? Steem is the name of the blockchain that stores all of the data and transactions, and processes all of the events that take place. STEEM is also a name for the system’s value token (currency). @@ -1149,7 +1149,7 @@ Steem is the name of the blockchain that stores all of the data and transactions Steemit is a front end web interface to interact with the blockchain, and view the blockchain data. ^ - + ## How is Steem different from Bitcoin? On a technical level, the two networks rely on the same model of a blockchain, but are built upon different technologies and codebase. Steem is based on a new state-of-the-art blockchain technology called Graphene, which uses "witnesses" instead of "miners" to produce blocks. @@ -1157,7 +1157,7 @@ On a technical level, the two networks rely on the same model of a blockchain, b The "delegated proof of stake" model of using witnesses instead of miners allows for greater efficiency in block production. With BTC, 100% of the new coins that are created are allocated to block producers (miners). With the Steem blockchain, only 10% of the new coins are paid to block producers (witnesses). The other 90% of new STEEM coins are awarded to content producers, curators, and STEEM Power holders. ^ - + ## What is the difference between Proof of Work, Proof of Stake, and Delegated Proof of Stake? **Proof of work** - Miners solve a complex mathematical problem. The miner that solves the problem first adds the block to the blockchain. The network rewards the miner for doing so. @@ -1167,25 +1167,25 @@ The "delegated proof of stake" model of using witnesses instead of miners allows **Delegated proof of stake** - Block-creating accounts, called witnesses, are collectively approved by Steem stakeholders. Instead of relying on proof of work to find blocks, the Steem network actively schedules these accounts to improve the time between blocks to 3 seconds. ^ - + ## How often does the Steem blockchain produce a new block? The Steem blockchain schedules witnesses to produce a new block every 3 seconds. 21 witness nodes produce 21 blocks in each 63-second round. ^ - + ## Is there a way to see the raw data that is stored in the blockchain? Yes. The blockchain data can be viewed in different ways with third-party tools such as steemd.com and steemdb.com. ^ - + ## Where can I find the information for the official launch of the blockchain? The original launch of Steem was on March 23, 2016, announced on Bitcointalk.org. There was a bug found in the original code though, and a majority of the stakeholders agreed that it would be easier to fix via a re-launch than a hardfork. The blockchain was reset and officially re-launched on March 24, 2016, via Bitcointalk.org. ^ - + ## Can I mine STEEM? No. Proof of work mining has been removed from Steem. @@ -1193,14 +1193,14 @@ No. Proof of work mining has been removed from Steem. ^ # Steemit, Inc. - + ## Who is the CEO of Steemit? Ned Scott, @ned https://www.linkedin.com/in/nedscott ^ - + ## Can I invest in Steemit? Steemit, Inc. is a privately held company and is not available for public investment. @@ -1208,32 +1208,32 @@ Steemit, Inc. is a privately held company and is not available for public invest Though not considered an investment, you can purchase STEEM tokens which can go up or down in value. You can power up these tokens into STEEM Power, which grants more influence in the Steem platform. ^ - + ## What does Steemit’s development roadmap look like? You can view the 2017 Roadmap here: https://steemit.com/steemit/@steemitblog/steemit-2017-roadmap ^ - + ## Am I allowed to use the Steemit logo? Currently, the Steem and Steemit logos are the same and is free to use. In the future, Steemit, Inc. will have its own logo so that it can be distinguished from Steem. The Steemit logo will be proprietary while Steem and its three S-shaped squiggles will remain open for public use. ^ - + ## Can I purchase official Steemit merchandise? Yes. Official Steemit merchandise can be purchased from [The Steemit Shop](https://thesteemitshop.com/). ^ - + ## Did Steemit "pre-mine" tokens? The STEEM tokens mined by Steemit, Inc. were not "pre-mined". All mining took place after the coin was officially and publicly announced on Bitcointalk.org. ^ - + ## What is the Steemit Privacy Policy? https://steemit.com/privacy.html @@ -1241,7 +1241,7 @@ https://steemit.com/privacy.html ^ # Security - + ## How can I keep my Steem account secure? Save your master password and keep it somewhere safe. @@ -1256,19 +1256,19 @@ Again, save your master password and keep it safe! If logging in with you It is not recommended to share your password or keys with any third party site. Steemit Inc. is developing a login application that can be used on third party Steem front ends. ^ - + ## Why should I be careful with my master password? The master password is used to derive all keys for your account, including the owner key. ^ - + ## Why is the master password a long string of gibberish? The password has to be long and random for maximum account security. ^ - + ## What are my different keys for? **Posting key** - The posting key allows accounts to post, comment, edit, vote, resteem, and follow or mute other accounts. Most users should be logging into Steemit every day with the posting key. You are more likely to have your password or key compromised the more you use it so a limited posting key exists to restrict the damage that a compromised account key would cause. @@ -1280,7 +1280,7 @@ The password has to be long and random for maximum account security. **Owner key** - The owner key is only meant for use when necessary. It is the most powerful key because it can change any key of an account, including the owner key. Ideally it is meant to be stored offline, and only used to recover a compromised account. ^ - + ## What do I do if I lost my password/keys? There is no way to recover your account if you lose your password or owner key! Because your account has real value, it is **very important** that you save your master password somewhere safe where you will not lose it. @@ -1288,19 +1288,19 @@ There is no way to recover your account if you lose your password or owner key! It is strongly recommended that you store an offline copy of your password somewhere safe in case of a hard drive failure or other calamity. Consider digital offline storage, such as an external disk or flash drive, as well as printed paper. Use a safe deposit box for best redundancy. ^ - + ## Are my STEEM and Steem Dollar tokens insured in the event of a hack or if someone takes over my account? No, liquid tokens can not be taken back if stolen or sent to the wrong account. If your tokens are in STEEM Power, it is impossible for a hacker to take out more than 1/13 per week. If your tokens are in savings, there is a three-day wait period for them to become transferable. ^ - + ## What should I do if I discover that someone hacked my account? If you made your account through Steemit and it is compromised, immediately visit the Stolen Account Recovery page. This link is also available in the main site menu. You will need to provide the email address that you used when you signed up, your account name, and a master password that was used in the last 30 days. ^ - + ## How does the stolen account recovery process work? If your password has been changed without your consent, then the account designated as your recovery account can generate a new owner key for the account. The account recovery must be completed within 30 days of the password being changed, and you must supply a recent owner key that was valid within the last 30 days. @@ -1310,7 +1310,7 @@ Steemit Inc. owns the default recovery account (@steem) for all users who sign u If you don't have the master password or owner key that was valid the past 30 days, or are unable to prove that you are the original owner of the account, then your account will be unrecoverable. ^ - + ## How do I report a security vulnerability? If you find a security issue please report the details to security@steemit.com. @@ -1318,7 +1318,7 @@ If you find a security issue please report the details to security@steemit.com. ^ # Developers - + ## Are the Steem blockchain and Steemit.com code open-source? Yes. Both the Steem blockchain and Steemit.com are open-source projects. @@ -1326,19 +1326,19 @@ Yes. Both the Steem blockchain and Steemit.com are open-source projects. Developers should however avoid the use of the term "Steemit" in their own products, and instead refer to the Steem Blockchain or Steem Platform. Steemit refers to Steemit.com, which is owned by Steemit, Inc. ^ - + ## Is there a Github page for Steemit.com? https://github.com/steemit/condenser ^ - + ## Is there a Github page for the Steem blockchain? https://github.com/steemit/steem ^ - + ## What is available for developers interested in Steem and Steemit? Many software engineers are currently leveraging the open-source code to build their applications on Steem. There are more than sixty so far. @@ -1347,7 +1347,7 @@ This post from the user @fabien has more information about the Steem API: https://steemit.com/steemjs/@fabien/steem-api-now-released ^ - + ## How do I use cli_wallet? Here is a guide from the user @pfunk explaining how to use the cli_wallet: @@ -1356,7 +1356,7 @@ https://steemit.com/steemhelp/@pfunk/a-learner-s-guide-to-using-steem-s-cliwalle ^ # Witnesses - + ## What are Steem witnesses? The Steem blockchain requires a set of people to create blocks and uses a consensus mechanism called delegated proof of stake, or DPOS. The community elects 'witnesses' to act as the network's block producers and governance body. There are 20 full-time witnesses, producing a block every 63-second round. A 21st position is shared by backup witnesses, who are scheduled proportionally to the amount of stake-weighted community approval they have. Witnesses are compensated with STEEM Power for each block they create. @@ -1364,13 +1364,13 @@ The Steem blockchain requires a set of people to create blocks and uses a consen Steemit leverages Steem because the founders of Steemit believe Steem’s decentralized text content storage and governance model makes Steem an excellent platform for supporting the long term success of its social network and digital currency tokens. ^ - + ## How can I vote for witnesses? Visit https://steemit.com/~witnesses ^ - + ## How many witnesses can I vote for? Each account can vote for up to 30 witnesses. @@ -1378,25 +1378,25 @@ Each account can vote for up to 30 witnesses. ^ # Miscellaneous - + ## What third-party tools are there for Steemit? http://steemtools.com/ ^ - + ## Is there an official Steemit Facebook page? https://www.facebook.com/steemit/ ^ - + ## Is there an official Steemit Twitter account? https://twitter.com/steemit ^ - + ## What is the Steem Whitepaper and what is its purpose? The Steem Whitepaper was written to describe the mechanics of the token system that makes decentralized content incentives and distribution possible in a way that can improve web technologies across the board. It is also applicable to Steemit, the first website to plug into the Steem blockchain. Users who have read the Steem Whitepaper will better understand how their interactions with Steemit are interactions with Steem, the decentralized network. @@ -1406,7 +1406,7 @@ It is worth noting that the Whitepaper hasn’t been updated almost since Steem https://steem.io/SteemWhitePaper.pdf ^ - + ## Where can I ask for help if my question was not answered here? If you post your question in the #help channel on steemit.chat, the users there may be able to help. @@ -1414,10 +1414,10 @@ If you post your question in the #he You can also create a post on Steemit.com with the tag #help, and someone in the community may be able to answer it. ^ - + # Disclaimer - + ## Third Party References and User Links BlockTrades, Poloniex, Bittrex, Changelly, Shapeshift.io, Coinbase, Localbitcoins, SteemDB, PeerHub, Steemit.chat, SteemTools, AnonSteem, SteemConnect, Streemian, SteemStats, Pixabay, Steemcleaners, Pexels, Postimage, Markdown Cheatsheet, @cheetah, Bitcointalk, bitcoinfees, blockchain.info, and steemd are third party applications/services, and are not owned or maintained by Steemit, Inc. Their listing here, as well as any other third party applications or websites that are listed, does not constitute and endorsement or recommendation on behalf of Steemit, Inc. -- GitLab From 6911d4d520ef144e2fe8c595d54777c3e08b04d0 Mon Sep 17 00:00:00 2001 From: TimCliff Date: Wed, 29 Nov 2017 20:40:53 -0600 Subject: [PATCH 082/399] Update welcome.md change tag names to ids --- src/app/help/en/welcome.md | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/app/help/en/welcome.md b/src/app/help/en/welcome.md index 3ddb28830..ea19b4eed 100644 --- a/src/app/help/en/welcome.md +++ b/src/app/help/en/welcome.md @@ -8,7 +8,7 @@ Below that is a section of "Helpful Posts from Steemit Users", which contains a Below that is a list of recommended users to follow, a collection of other resources including the FAQ Page, and information on where to find live help. -## Table of Contents +## Table of Contents ### Quick Start Guide @@ -48,13 +48,13 @@ Below that is a list of recommended users to follow, a collection of other resou ## Quick Start Guide - + ### No Cost to Participate It is free to post, comment, or upvote all content on Steemit.com. You might even get paid for it! ^ - + ### Upvotes Upvotes are Steemit's way of saying you like someone's post or comment. @@ -62,7 +62,7 @@ Upvotes are Steemit's way of saying you like someone's post or comment. To upvote, click on the *Upvote* icon at the bottom of the comment/post. ^ - + ### Comments When you are first starting out, commenting on other people's posts can be a great way to get involved and connect with people! @@ -70,7 +70,7 @@ When you are first starting out, commenting on other people's posts can be a gre To comment on a post, or reply to an existing comment, click on the "Reply" link at the bottom of the post/comment. ^ - + ### Creating Posts To create a post, click on the "Post" link in the upper right corner. @@ -84,7 +84,7 @@ To create your content, you can either use "Editor" or "Markdown" mode. There are several guides for creating posts in the "Helpful Posts from Steemit Users" section below. ^ - + ### Tags Tags will help people find your posts. @@ -98,7 +98,7 @@ The tags should all be relevant to the content in the post. You can browse content by tags, as well as see a list of popular tags that other users have used in their posts [here](https://steemit.com/tags). ^ - + ### Followers and Feeds To follow an author, click on their username and click the "Follow" button. @@ -110,13 +110,13 @@ As other Steemians come across your posts and comments, you will start to gain f You can see all of your followers and the people you are following in your profile page. ^ - + ### Resteem If you want to share someone else's post with all of your followers, click on the *resteem* icon. ^ - + ### Digital Currencies STEEM, Steem Power and Steem Dollars are the three forms of digital currency used by the Steem Blockchain. @@ -124,7 +124,7 @@ STEEM, Steem Power and Steem Dollars are the three forms of digital currency use More information on the three types of tokens can be found in the [Steemit FAQ](https://steemit.com/faq.html). ^ - + ### Curation Up to 25% of the reward for posts goes to the people who voted on it. These people are called curators. @@ -132,7 +132,7 @@ Up to 25% of the reward for posts goes to the people who voted on it. These peop The more Steem Power you have in your account, the more your upvotes will be worth, and the more potential curation rewards you can earn! ^ - + ### Payments Payouts are made 7 days after the post/comment is created. You can claim your rewards in your wallet after 7 days. @@ -146,7 +146,7 @@ The author reward is paid 50% in Steem Power, and 50% in liquid STEEM/SBD. Authors also have the option to decline payout, or be paid in 100% Steem Power! ^ - + ### Home, New, Hot, Trending, Promoted, and Active These are various ways to sort blog posts. @@ -162,7 +162,7 @@ These are various ways to sort blog posts. **Promoted** - Listings that are boosted by Steem Dollar payments get "Promoted" for greater visibility. ^ - + ### Profile @@ -184,7 +184,7 @@ These are various ways to sort blog posts. **Logout** - Here is where you go to logout. ^ - + ### Reputation A reputation score is one way Steemit measures the amount of value you have brought to the community. @@ -196,7 +196,7 @@ All new users start at 25. Your reputation will go up as you earn upvotes for your posts and comments, but it can come down if they are flagged. ^ - + ### Cashing out or Spending SBD You can spend your SBD at the [Peerhub Store](https://www.peerhub.com/). @@ -206,7 +206,7 @@ You can exchange your STEEM and SBD for bitcoin on an exchange such as [BlockTra You can also "Power Up" and use your STEEM/SBD to gain more Steem Power! ^ - + ### Plagiarism The community is looking for you to add your own personal touch to your articles. @@ -218,7 +218,7 @@ If you are using anyone else's material as part of your posts (including images) Also, make sure that you are not violating any copyright laws if you are using someone else's material/images. Limited, sourced material sharing is OK under fair use and fair dealing doctrines. ^ - + ### Password Security Your Steemit account is worth real money. Treat your Steemit password like you would your bank password, and keep it secure! @@ -226,7 +226,7 @@ Your Steemit account is worth real money. Treat your Steemit password like you w Unless your password was recently changed and you possess the old one, **there is no password recovery for Steem accounts**. You are 100% responsible for having it backed up. This means secure digital backups, as well as secured paper backups, off-site if possible. ^ - + ### Earning on Steemit The best attitude to have is to expect to make nothing. Have fun. Get engaged. Make friends. If along the way you earn something - bonus! @@ -239,7 +239,7 @@ It is possible to earn thousands of dollars, but most authors who are doing this ## To Do List - + ### 1. Backup your password Unlike centralized web services, **the Steem Blockchain has no account password recovery**. @@ -253,7 +253,7 @@ It is strongly recommended that you store an offline copy of your password somew If your account is valuable, treat it like a valuable! ^ - + ### 2. Sign Up for Steemit Chat A lot of users hang out and chat when they are not posting or browsing Steemit. It is a great place to meet people! @@ -267,7 +267,7 @@ Some channels allow you to share links, but others don't. For instance, [general Each channel will have its rules posted in the "Info" section. ^ - + ### 3. Setup your Profile, Avatar, and Cover Image Under your user settings, you can update your profile. This includes your display name, location, about info, and website. @@ -279,7 +279,7 @@ To set your cover image, type or paste a link to the URL where the image is loca Once you have made all your changes, click the "Update" button to save your profile. ^ - + ### 4. Choose your "NSFW" (Not Safe for Work) Display Preference By default, content that users have tagged as "NSFW" will be hidden, but a link will be shown to reveal the content. @@ -287,7 +287,7 @@ By default, content that users have tagged as "NSFW" will be hidden, but a link You can update your display preference so that NSFW content is always shown by default, or is completely hidden with no option to reveal. ^ - + ### 5. Create your "introduceyourself" post While not required, the tradition for new users is to create an "introduceyourself" post, to let the community know who you are. @@ -302,7 +302,7 @@ It is not required either, but if you have other social media accounts (Twitter, *** -## Helpful Posts from Steemit Users +## Helpful Posts from Steemit Users - [Posting and Markdown Basics](https://steemit.com/steemit/@thecryptofiend/markdown-basics-for-beginners) - [Tons of Ways to Spend Your Hard Earned STEEM/SBD](https://steemit.com/steem/@timcliff/the-steem-economy-tons-of-ways-to-spend-your-hard-earned-steem-sbd) @@ -338,14 +338,14 @@ It is not required either, but if you have other social media accounts (Twitter, - [How to Create Different Types of Blog Content](https://steemit.com/writing/@jessicanicklos/how-to-create-different-types-of-blog-content-know-here-total-guide-line) ^ - + ## Users to Follow - @steemitblog - Official Steemit Announcements - @ned - Ned Scott, CEO and Co-Founder of Steemit ^ - + ## Other Resources - [FAQ](https://steemit.com/faq.html) @@ -356,7 +356,7 @@ It is not required either, but if you have other social media accounts (Twitter, - [Steem Blockchain Explorer](https://steemdb.com/) ^ - + ## Live Help Ask your general questions in the [help](https://steemit.chat/channel/help) channel of [steemit.chat](https://steemit.chat/home). Users in the channel will typically respond to questions within a few hours. @@ -364,7 +364,7 @@ Ask your general questions in the [help](https://steemit.chat/channel/help) chan New Member Support Community is a group of people dedicated to helping new users find their way around Steemit. You can find them in the [New Member Support Community](https://discord.gg/HYj4yvw) channel of Discord Chat. ^ - + ## Third Party References Peerhub, BlockTrades, Bittrex, Steemit Chat, Steemit Help, New Member Support Community, and Discord Chat, as well as the tools listed under "Other Resources" are third party applications/services, and are not owned or maintained by Steemit, Inc. Their listing here does not constitute and endorsement or recommendation on behalf of Steemit, Inc. -- GitLab From ac155007ad2642dc5a5995715d2d7b63210b2f3e Mon Sep 17 00:00:00 2001 From: TimCliff Date: Wed, 29 Nov 2017 21:54:24 -0500 Subject: [PATCH 083/399] fix .anchor cursor style --- src/app/assets/stylesheets/app.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/assets/stylesheets/app.scss b/src/app/assets/stylesheets/app.scss index d963cd159..ef2c69447 100644 --- a/src/app/assets/stylesheets/app.scss +++ b/src/app/assets/stylesheets/app.scss @@ -175,6 +175,7 @@ a.ptc { position: relative; display: block; z-index:-1000; + cursor: default; } h1, h2, h3, h4, h5, h6 { -- GitLab From a5c65b0a4af4a762bc36060b08b5a42c87a177da Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Wed, 29 Nov 2017 19:33:06 -0800 Subject: [PATCH 084/399] update language --- src/app/help/en/faq.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index 3d71ad27d..527267761 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1221,8 +1221,6 @@ The Steemit brand and logo are protected by intellectual property laws, includin [Section 107 of the Copyright Act](http://www.copyright.gov/title17/92chap1.html#107) provides the statutory framework for determining whether something is a fair use and identifies certain types of uses—such as criticism, comment, news reporting, teaching, scholarship, and research—as examples of activities that may qualify as fair use. **Steemit considers artistic variations of the Steemit logo that are used for non-commercial purposes, and are not used to harm Steemit users (i.e. through attraction to phishing sites), an instance of fair use.** -Steemit also considers posts which are created on the Steem blockchain and earn rewards through the blockchain rewards mechanism an authorized form of commercial use. - The blue Steem logo is licensed under Creative Commons CC0, meaning you’re free to copy, modify, or distribute (even for commercial purposes) without needing to ask permission or give attribution. ^ -- GitLab From d1dfb00c5eeaeb2fbe7d51ab0bbdc1988259beb1 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Wed, 29 Nov 2017 19:37:34 -0800 Subject: [PATCH 085/399] Clarify Steem logo part 2 --- src/app/help/en/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index 527267761..e059cdaf8 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1221,7 +1221,7 @@ The Steemit brand and logo are protected by intellectual property laws, includin [Section 107 of the Copyright Act](http://www.copyright.gov/title17/92chap1.html#107) provides the statutory framework for determining whether something is a fair use and identifies certain types of uses—such as criticism, comment, news reporting, teaching, scholarship, and research—as examples of activities that may qualify as fair use. **Steemit considers artistic variations of the Steemit logo that are used for non-commercial purposes, and are not used to harm Steemit users (i.e. through attraction to phishing sites), an instance of fair use.** -The blue Steem logo is licensed under Creative Commons CC0, meaning you’re free to copy, modify, or distribute (even for commercial purposes) without needing to ask permission or give attribution. +The blue Steem logo with the three S-shaped squiggles is licensed under Creative Commons CC0, meaning you’re free to copy, modify, or distribute (even for commercial purposes) without needing to ask permission or give attribution. ^ -- GitLab From 0a6c5118307e3e4ade94bc27620dd1e7fa7b2907 Mon Sep 17 00:00:00 2001 From: Originate Date: Wed, 15 Nov 2017 19:46:26 -0800 Subject: [PATCH 086/399] Improve engagement display reply box automatically for logged in users, eslint misc cleanups from other code --- src/app/components/cards/PostFull.jsx | 29 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/app/components/cards/PostFull.jsx b/src/app/components/cards/PostFull.jsx index 549490f5b..3628068de 100644 --- a/src/app/components/cards/PostFull.jsx +++ b/src/app/components/cards/PostFull.jsx @@ -115,9 +115,12 @@ class PostFull extends React.Component { formId, PostFullReplyEditor: ReplyEditor(formId + '-reply'), PostFullEditEditor: ReplyEditor(formId + '-edit') - }) + }); if (process.env.BROWSER) { - let showEditor = localStorage.getItem('showEditor-' + formId) + let showEditor = localStorage.getItem('showEditor-' + formId); + if (this.props.username) { + this.setState({showReply: true}); + } if (showEditor) { showEditor = JSON.parse(showEditor) if (showEditor.type === 'reply') { @@ -136,6 +139,12 @@ class PostFull extends React.Component { this.state !== nextState } + componentWillReceiveProps(nextProps) { + if (nextProps.username && process.env.BROWSER) { + this.setState({showReply: true}); + } + } + fbShare(e) { const href = this.share_params.url; e.preventDefault(); @@ -189,13 +198,13 @@ class PostFull extends React.Component { render() { const {props: {username, post}, state: {PostFullReplyEditor, PostFullEditEditor, formId, showReply, showEdit}, - onShowReply, onShowEdit, onDeletePost} = this + onShowReply, onShowEdit, onDeletePost} = this; const post_content = this.props.cont.get(this.props.post); if (!post_content) return null; const p = extractContent(immutableAccessor, post_content); const content = post_content.toJS(); - const {author, permlink, parent_author, parent_permlink} = content - const jsonMetadata = this.state.showReply ? null : p.json_metadata + const {author, permlink, parent_author, parent_permlink} = content; + const jsonMetadata = this.state.showReply ? null : p.json_metadata; // let author_link = '/@' + content.author; let link = `/@${content.author}/${content.permlink}`; if (content.category) link = `/${content.category}${link}`; @@ -204,9 +213,9 @@ class PostFull extends React.Component { if (process.env.BROWSER && title) document.title = title + ' — '+ APP_NAME; let content_body = content.body; - const url = `/${category}/@${author}/${permlink}` + const url = `/${category}/@${author}/${permlink}`; const bDMCAStop = DMCAList.includes(url); - const bIllegalContentUser = userIllegalContent.includes(content.author) + const bIllegalContentUser = userIllegalContent.includes(content.author); if(bDMCAStop) { content_body = tt('postfull_jsx.this_post_is_not_available_due_to_a_copyright_claim') } @@ -215,12 +224,12 @@ class PostFull extends React.Component { content_body = 'Not available for legal reasons.' } - const bShowLoading = (!bIllegalContentUser && !bDMCAStop && content.body.length < content.body_length) + const bShowLoading = (!bIllegalContentUser && !bDMCAStop && content.body.length < content.body_length); // hide images if user is on blacklist - const hideImages = ImageUserBlockList.includes(content.author) + const hideImages = ImageUserBlockList.includes(content.author); - const replyParams = {author, permlink, parent_author, parent_permlink, category, title, body} + const replyParams = {author, permlink, parent_author, parent_permlink, category, title, body}; this.share_params = { link, -- GitLab From 23f261931a0a31f3e5e721304ea7b12c9fcf5ee9 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Thu, 30 Nov 2017 13:46:12 -0500 Subject: [PATCH 087/399] fixes #2074 only show reply box on initial render for logged-in user --- src/app/components/cards/PostFull.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/cards/PostFull.jsx b/src/app/components/cards/PostFull.jsx index 3628068de..e12e3bfda 100644 --- a/src/app/components/cards/PostFull.jsx +++ b/src/app/components/cards/PostFull.jsx @@ -140,7 +140,7 @@ class PostFull extends React.Component { } componentWillReceiveProps(nextProps) { - if (nextProps.username && process.env.BROWSER) { + if (!this.props.username && nextProps.username && process.env.BROWSER) { this.setState({showReply: true}); } } -- GitLab From 934bf5e4d211f34125b15d1a9ac428ed5027c466 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Thu, 30 Nov 2017 16:49:27 -0500 Subject: [PATCH 088/399] Revert "Merge pull request #2075 from steemit/2074-only-open-commentbox-once" This reverts commit 0831ff890db65e53323db77ea7ac6cf1ad41f3de, reversing changes made to 40e76b8d7b743288d2cf15dc932b52fe7539a4b3. --- src/app/components/cards/PostFull.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/cards/PostFull.jsx b/src/app/components/cards/PostFull.jsx index e12e3bfda..3628068de 100644 --- a/src/app/components/cards/PostFull.jsx +++ b/src/app/components/cards/PostFull.jsx @@ -140,7 +140,7 @@ class PostFull extends React.Component { } componentWillReceiveProps(nextProps) { - if (!this.props.username && nextProps.username && process.env.BROWSER) { + if (nextProps.username && process.env.BROWSER) { this.setState({showReply: true}); } } -- GitLab From 544bcc62fd5d47cb981087f9c199bb493159c0b6 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Thu, 30 Nov 2017 16:49:46 -0500 Subject: [PATCH 089/399] Revert "Merge pull request #2003 from steemit/1465-article-comment-auto" This reverts commit 20fb681d12260ded25f28a44597bb2e7c0859b5f, reversing changes made to 93b7c312ed77a2096937bb245cc9370b8a897803. --- src/app/components/cards/PostFull.jsx | 29 +++++++++------------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/app/components/cards/PostFull.jsx b/src/app/components/cards/PostFull.jsx index 3628068de..549490f5b 100644 --- a/src/app/components/cards/PostFull.jsx +++ b/src/app/components/cards/PostFull.jsx @@ -115,12 +115,9 @@ class PostFull extends React.Component { formId, PostFullReplyEditor: ReplyEditor(formId + '-reply'), PostFullEditEditor: ReplyEditor(formId + '-edit') - }); + }) if (process.env.BROWSER) { - let showEditor = localStorage.getItem('showEditor-' + formId); - if (this.props.username) { - this.setState({showReply: true}); - } + let showEditor = localStorage.getItem('showEditor-' + formId) if (showEditor) { showEditor = JSON.parse(showEditor) if (showEditor.type === 'reply') { @@ -139,12 +136,6 @@ class PostFull extends React.Component { this.state !== nextState } - componentWillReceiveProps(nextProps) { - if (nextProps.username && process.env.BROWSER) { - this.setState({showReply: true}); - } - } - fbShare(e) { const href = this.share_params.url; e.preventDefault(); @@ -198,13 +189,13 @@ class PostFull extends React.Component { render() { const {props: {username, post}, state: {PostFullReplyEditor, PostFullEditEditor, formId, showReply, showEdit}, - onShowReply, onShowEdit, onDeletePost} = this; + onShowReply, onShowEdit, onDeletePost} = this const post_content = this.props.cont.get(this.props.post); if (!post_content) return null; const p = extractContent(immutableAccessor, post_content); const content = post_content.toJS(); - const {author, permlink, parent_author, parent_permlink} = content; - const jsonMetadata = this.state.showReply ? null : p.json_metadata; + const {author, permlink, parent_author, parent_permlink} = content + const jsonMetadata = this.state.showReply ? null : p.json_metadata // let author_link = '/@' + content.author; let link = `/@${content.author}/${content.permlink}`; if (content.category) link = `/${content.category}${link}`; @@ -213,9 +204,9 @@ class PostFull extends React.Component { if (process.env.BROWSER && title) document.title = title + ' — '+ APP_NAME; let content_body = content.body; - const url = `/${category}/@${author}/${permlink}`; + const url = `/${category}/@${author}/${permlink}` const bDMCAStop = DMCAList.includes(url); - const bIllegalContentUser = userIllegalContent.includes(content.author); + const bIllegalContentUser = userIllegalContent.includes(content.author) if(bDMCAStop) { content_body = tt('postfull_jsx.this_post_is_not_available_due_to_a_copyright_claim') } @@ -224,12 +215,12 @@ class PostFull extends React.Component { content_body = 'Not available for legal reasons.' } - const bShowLoading = (!bIllegalContentUser && !bDMCAStop && content.body.length < content.body_length); + const bShowLoading = (!bIllegalContentUser && !bDMCAStop && content.body.length < content.body_length) // hide images if user is on blacklist - const hideImages = ImageUserBlockList.includes(content.author); + const hideImages = ImageUserBlockList.includes(content.author) - const replyParams = {author, permlink, parent_author, parent_permlink, category, title, body}; + const replyParams = {author, permlink, parent_author, parent_permlink, category, title, body} this.share_params = { link, -- GitLab From b5c8af0daba4bb3f4c54ca7ce5b104655661fce0 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Thu, 30 Nov 2017 15:43:28 -0800 Subject: [PATCH 090/399] Clarify license terms of use for Steemit logo --- doc/LICENSE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/LICENSE.md b/doc/LICENSE.md index c78b719a1..ec13ee2c4 100644 --- a/doc/LICENSE.md +++ b/doc/LICENSE.md @@ -5,3 +5,5 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +The Steemit logo file that is included in the repository is **not** included in the above license. The Steemit brand and logo are protected by intellectual property laws, including copyright and other proprietary rights of the United States and other countries. The purpose is to allow Steemit to protect the brand and logo in ways that extend user safety. One may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the Steemit logo or brand, except as permitted by the doctrine of fair use, or as authorized in writing by Steemit, Inc. -- GitLab From 62e3cfcc7de63f82bf5f46bd0286cc6d9faa50e6 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Thu, 30 Nov 2017 15:44:02 -0800 Subject: [PATCH 091/399] wording update --- doc/LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/LICENSE.md b/doc/LICENSE.md index ec13ee2c4..1d5e582c7 100644 --- a/doc/LICENSE.md +++ b/doc/LICENSE.md @@ -6,4 +6,4 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The Steemit logo file that is included in the repository is **not** included in the above license. The Steemit brand and logo are protected by intellectual property laws, including copyright and other proprietary rights of the United States and other countries. The purpose is to allow Steemit to protect the brand and logo in ways that extend user safety. One may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the Steemit logo or brand, except as permitted by the doctrine of fair use, or as authorized in writing by Steemit, Inc. +The Steemit logo file that is stored in the repository is **not** included in the above license. The Steemit brand and logo are protected by intellectual property laws, including copyright and other proprietary rights of the United States and other countries. The purpose is to allow Steemit to protect the brand and logo in ways that extend user safety. One may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the Steemit logo or brand, except as permitted by the doctrine of fair use, or as authorized in writing by Steemit, Inc. -- GitLab From 91bdf4c6496cc314a07aa9cf7493e65bad04ba59 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Thu, 30 Nov 2017 18:15:24 -0600 Subject: [PATCH 092/399] keep links beginning with '#' from being prefixed with 'https:' --- src/shared/HtmlReady.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/HtmlReady.js b/src/shared/HtmlReady.js index 8156ffd55..621589174 100644 --- a/src/shared/HtmlReady.js +++ b/src/shared/HtmlReady.js @@ -134,7 +134,7 @@ function link(state, child) { state.links.add(url) if(state.mutate) { // If this link is not relative, http, or https -- add https. - if(! /^\/(?!\/)|(https?:)?\/\//.test(url)) { + if(! /^((#)|(\/(?!\/))|((https?:)?\/\/))/.test(url)) { child.setAttribute('href', "https://"+url) } -- GitLab From d2088e2866850c408c3784b1e934b6e976a6620c Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Thu, 30 Nov 2017 18:16:52 -0600 Subject: [PATCH 093/399] Support in-page scroll 'up' & disable forward/back nav check --- src/shared/UniversalRender.jsx | 58 ++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index ae4cd9401..92b8494a4 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -45,7 +45,12 @@ const calcOffsetRoot = (startEl) => { const SCROLL_TOP_TRIES = 50; const SCROLL_TOP_DELAY_MS = 100; const SCROLL_TOP_EXTRA_PIXEL_OFFSET = 3; -const SCROLL_UP_FUDGE_PIXELS = 10; +const SCROLL_FUDGE_PIXELS = 10; +const SCROLL_DIRECTION_UP = 'up'; +const SCROLL_DIRECTION_DOWN = 'down'; + +//If an element with this id is present, the page does not want us to detect whether we're navigating forward or not +const DISABLE_ROUTER_HISTORY_NAV_DIRECTION_EL_ID = 'disable_router_nav_history_direction_check'; let scrollTopTimeout = null; @@ -64,15 +69,23 @@ const scrollTop = (el, topOffset, prevDocumentInfo, triesRemaining) => { const documentInfo = { scrollHeight: document.body.scrollHeight, scrollTop: Math.ceil(document.scrollingElement.scrollTop), - scrollTarget: calcOffsetRoot(el) + topOffset + scrollTarget: calcOffsetRoot(el) + topOffset, + direction: prevDocumentInfo.direction, }; - - if(prevDocumentInfo.scrollTop > (documentInfo.scrollTop + SCROLL_UP_FUDGE_PIXELS)) { //detecting that the user has scrolled in an up direction - return; + let doScroll = false; + if(SCROLL_DIRECTION_DOWN === prevDocumentInfo.direction) { + doScroll = (!(prevDocumentInfo.scrollTop > (documentInfo.scrollTop + SCROLL_FUDGE_PIXELS)) + && (documentInfo.scrollTop < documentInfo.scrollTarget + || prevDocumentInfo.scrollTarget < documentInfo.scrollTarget + || prevDocumentInfo.scrollHeight < documentInfo.scrollHeight)); + } else if(SCROLL_DIRECTION_UP === prevDocumentInfo.direction) { + doScroll = (!(prevDocumentInfo.scrollTop < (documentInfo.scrollTop - SCROLL_FUDGE_PIXELS)) + && (documentInfo.scrollTop > documentInfo.scrollTarget + || prevDocumentInfo.scrollTarget > documentInfo.scrollTarget + || prevDocumentInfo.scrollHeight > documentInfo.scrollHeight)); } - if(documentInfo.scrollTop < documentInfo.scrollTarget - || prevDocumentInfo.scrollTarget < documentInfo.scrollTarget - || prevDocumentInfo.scrollHeight < documentInfo.scrollHeight) { + + if(doScroll) { window.scrollTo(0, documentInfo.scrollTarget); if(triesRemaining > 0) { scrollTopTimeout = setTimeout(() => scrollTop(el, topOffset, documentInfo, (triesRemaining-1)), SCROLL_TOP_DELAY_MS); @@ -86,18 +99,34 @@ const scrollTop = (el, topOffset, prevDocumentInfo, triesRemaining) => { class OffsetScrollBehavior extends ScrollBehavior { scrollToTarget(element, target) { clearTimeout(scrollTopTimeout); - const el = (typeof target === 'string') ? document.getElementById(target) : false; + const header = document.getElementsByTagName('header')[0]; //this dimension ideally would be pulled from a scss file. + const topOffset = (((header)? header.offsetHeight : 0) + SCROLL_TOP_EXTRA_PIXEL_OFFSET) * (-1); + const newTarget = []; //x coordinate + let el = false; + if(typeof target === 'string' ) { + el = document.getElementById(target.substr(1)); + if(!el) { + el = document.getElementById(target); + } + } else { + newTarget.push(target[0]); + if((target[1] + topOffset) > 0) { + newTarget.push(target[1] + topOffset); + } else { + newTarget.push(0); + } + } + if(el) { - const header = document.getElementsByTagName('header')[0]; //this dimension ideally would be pulled from a scss file. - const topOffset = (((header)? header.offsetHeight : 0) + SCROLL_TOP_EXTRA_PIXEL_OFFSET) * (-1); const documentInfo = { scrollHeight: document.body.scrollHeight, scrollTop: Math.ceil(document.scrollingElement.scrollTop), - scrollTarget: 0 + scrollTarget: calcOffsetRoot(el) + topOffset, }; + documentInfo.direction = documentInfo.scrollTop < documentInfo.scrollTarget ? SCROLL_DIRECTION_DOWN : SCROLL_DIRECTION_UP; scrollTop(el, topOffset, documentInfo, SCROLL_TOP_TRIES); } else { - super.scrollToTarget(element, target); + super.scrollToTarget(element, newTarget); } } } @@ -167,7 +196,8 @@ async function universalRender({location, initial_state, offchain, ErrorPage, ta shouldUpdateScroll: (prevLocation, {location}) => { // eslint-disable-line no-shadow //we want to navigate to the corresponding id= element on 'PUSH' navigation (prev null + POP is a new window url nav ~= 'PUSH') if(location.hash) { - if((prevLocation === null && location.action === 'POP') + const disableNavDirectionCheck = document.getElementById(DISABLE_ROUTER_HISTORY_NAV_DIRECTION_EL_ID); + if(disableNavDirectionCheck || (prevLocation === null && location.action === 'POP') || (location.action === 'PUSH') ) { return location.hash; -- GitLab From c28459e067c690cbf5377d994589cd375df62173 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Thu, 30 Nov 2017 18:28:10 -0600 Subject: [PATCH 094/399] Make in-page links relative. Replace anchor < a > with < span > for ### & ## --- src/app/assets/stylesheets/app.scss | 1 - src/app/help/en/faq.md | 1134 ++++++++++++--------------- src/app/help/en/welcome.md | 194 ++--- 3 files changed, 577 insertions(+), 752 deletions(-) diff --git a/src/app/assets/stylesheets/app.scss b/src/app/assets/stylesheets/app.scss index ef2c69447..388253c02 100644 --- a/src/app/assets/stylesheets/app.scss +++ b/src/app/assets/stylesheets/app.scss @@ -174,7 +174,6 @@ a.ptc { margin-top: -68px; position: relative; display: block; - z-index:-1000; cursor: default; } diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index 78c45b31d..332a3221e 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1,223 +1,220 @@ + # Steemit FAQ -## Table of Contents - -### General -- What is Steemit.com? -- How does Steemit work? -- How does Steemit differ from other social media websites? -- Does it cost anything to post, comment, or vote? -- Can I earn digital tokens on Steemit? How? -- Where do the tokens come from? -- Where does the value come from? -- Why are people getting vastly different rewards? - -### Accounts -- How do I create an account? -- What information do I need to provide in order to create an account? -- How long does the account approval process take? -- Why do I need to provide my email and phone number? -- Can I create a Steem account without an email and phone number? -- What are other ways to create an account on the blockchain besides using Steemit.com? -- It is not letting me create an account with my phone number. What should I do? -- What happens if my email or phone number changes? -- Am I allowed to create more than one account -- Can I delete or deactivate my account? - -### Community -- Is there an Etiquette Guide for Steemit? -- Am I required to verify my identity? - -### Site Navigation -- How do I upvote a post or comment? -- What do the Home, New, Hot, Trending, and Promoted links show? -- What information is available in my account menu? -- How do I see my recent rewards? -- What information is shown in my wallet? -- How do I transfer my STEEM or Steem Dollars into savings? -- How do I send money to another user? -- Will I receive notifications when there is activity with my account? -- What is shown in my profile? -- How do I change my avatar image and other profile information? -- What is the recommend size for the cover image? -- How can I control whether I see "Not Safe For Work" (NSFW) content? -- How do I search for content? -- Can I see which users I have muted? -- Can I see which users have muted me? -- Can I see the list of users I am following, and who is following me? -- What languages are supported? - -### Posting -- What can users post to Steemit? -- What are the different choices for post rewards (50%/50%, Power Up 100%, Decline Payout)? -- How do I add images and photos to my posts? -- How do I set the thumbnail image for my post? -- What is the recommend aspect ratio for thumbnail images? -- How do I add videos to my posts? -- Is there a way I can make my images smaller? -- What are tags? -- What tags should I use? -- How many tags can I use? -- Why is the "Post" button grayed out? -- How do I format text in Markdown? -- How often can I post? -- How long can my post be? -- If posting in a language other than English, how will I get recognized? -- Can I delete something I posted? -- What does "Promoting" a post do? -- How do I promote a post? - -### Comments -- Can I earn digital tokens for commenting? -- How often can I comment? - -### Economics -- Where do the new STEEM tokens come from? -- How many new tokens are generated by the blockchain? -- How are the new tokens distributed? -- What is the reward pool? -- How is the reward pool split between authors and curators? -- Will the reward pool pay out more or less depending on who votes? -- Why do the earnings for my post go up or down? -- When can I claim my rewards? -- What is the difference between STEEM, STEEM Power, and Steem Dollars? -- What is delegated STEEM Power? -- What determines the price of STEEM? -- How do I get more STEEM Power? -- How long does it take STEEM or STEEM Power that I purchased to show up in my account? -- What is powering up and down? -- What do the dollar amounts for pending payouts represent? -- Will 1 Steem Dollar always be worth $1.00 USD? -- How do Steem Dollar to STEEM conversions work? -- Is there a way for me to convert my Steem Dollars to STEEM without waiting 3.5 days? -- What can I do with my STEEM tokens? -- What can I do with my SBD tokens? -- What is a MVEST? -- Can I sell goods and services on Steemit? -- How can I withdraw my STEEM or SBD coins? -- Will I get a 1099 from Steemit? -- How much are the transaction fees for sending tokens to other users? -- Are there fees for Powering Up, Powering Down, trading on the internal market, or converting SBD to STEEM? -- How long does it take to transfer STEEM or SBD tokens between users? - -### Voting and Curating -- What is my voting power? -- How many times can I vote without depleting my voting power? -- Can I vote with less than 100% of my voting strength? -- Where can I check my voting power? -- What determines how much of the curation reward goes to the author versus curators? -- Can I get curation rewards for upvoting comments? -- Do I get curation rewards for downvoting posts or comments? -- What are curation trails? -- Why don't my upvotes have an effect on a post's rewards? -- Is there a way to make my votes count for more? -- What are the valid reasons for downvoting? -- Does a downvote mean that I did something wrong? -- Will a downvote hurt my reputation? -- What is the difference between a downvote and a flag? - -### Plagiarism, Spam, and Abuse -- What is considered spam or abuse? -- What are Steemit’s policies on plagiarism? -- Is it okay to use random pictures from the internet? -- What is Steemcleaners? -- What is @cheetah? -- Where do I report a post or comment that contains plagiarism, spam, or abuse? - -### Reputation -- What is Reputation? -- How is the Reputation score measured? -- How do I improve my reputation score? -- What causes my reputation score to go down? -- Why does my reputation score matter? - -### Followers, Feeds, and Resteem -- What is Resteeming? -- Can I share on other social media? - -### Blockchain -- What is a blockchain? -- What is the Steem blockchain? -- What is the difference between Steem and Steemit? -- How is Steem different from Bitcoin? -- What is the difference between Proof of Work, Proof of Stake, and Delegated Proof of Stake? -- How often does the Steem blockchain produce a new block? -- Is there a way to see the raw data that is stored in the blockchain? -- Where can I find the information for the official launch of the blockchain? -- Can I mine STEEM? - -### Steemit, Inc. -- Who is the CEO of Steemit? -- Can I invest in Steemit? -- What does Steemit’s development roadmap look like? -- Am I allowed to use the Steemit logo? -- Can I purchase official Steemit merchandise? -- Did Steemit "pre-mine" tokens? -- What is the Steemit Privacy Policy? - -### Security -- How can I keep my Steem account secure? -- Why should I be careful with my master password? -- Why is the master password a long string of gibberish? -- What are my different keys for? -- What do I do if I lost my password/keys? -- Are my STEEM and Steem Dollar tokens insured in the event of a hack or if someone takes over my account? -- What should I do if I discover that someone hacked my account? -- How does the stolen account recovery process work? -- How do I report a security vulnerability? - -### Developers -- Are the Steem blockchain and Steemit.com code open-source? -- Is there a Github page for Steemit.com? -- Is there a Github page for the Steem blockchain? -- What is available for developers interested in Steem and Steemit? -- How do I use cli_wallet? - -### Witnesses -- What are Steem witnesses? -- How can I vote for witnesses? -- How many witnesses can I vote for? - -### Miscellaneous -- What third-party tools are there for Steemit? -- Is there an official Steemit Facebook page? -- Is there an official Steemit Twitter account? -- What is the Steem Whitepaper and what is its purpose? -- Where can I ask for help if my question was not answered here? - -### Disclaimer -- Third Party References and User Links +## Table of Contents + +### General +- What is Steemit.com? +- How does Steemit work? +- How does Steemit differ from other social media websites? +- Does it cost anything to post, comment, or vote? +- Can I earn digital tokens on Steemit? How? +- Where do the tokens come from? +- Where does the value come from? +- Why are people getting vastly different rewards? + +### Accounts +- How do I create an account? +- What information do I need to provide in order to create an account? +- How long does the account approval process take? +- Why do I need to provide my email and phone number? +- Can I create a Steem account without an email and phone number? +- What are other ways to create an account on the blockchain besides using Steemit.com? +- It is not letting me create an account with my phone number. What should I do? +- What happens if my email or phone number changes? +- Am I allowed to create more than one account +- Can I delete or deactivate my account? + +### Community +- Is there an Etiquette Guide for Steemit? +- Am I required to verify my identity? + +### Site Navigation +- How do I upvote a post or comment? +- What do the Home, New, Hot, Trending, and Promoted links show? +- What information is available in my account menu? +- How do I see my recent rewards? +- What information is shown in my wallet? +- How do I transfer my STEEM or Steem Dollars into savings? +- How do I send money to another user? +- Will I receive notifications when there is activity with my account? +- What is shown in my profile? +- How do I change my avatar image and other profile information? +- What is the recommend size for the cover image? +- How can I control whether I see "Not Safe For Work" (NSFW) content? +- How do I search for content? +- Can I see which users I have muted? +- Can I see which users have muted me? +- Can I see the list of users I am following, and who is following me? +- What languages are supported? + +### Posting +- What can users post to Steemit? +- What are the different choices for post rewards (50%/50%, Power Up 100%, Decline Payout)? +- How do I add images and photos to my posts? +- How do I set the thumbnail image for my post? +- What is the recommend aspect ratio for thumbnail images? +- How do I add videos to my posts? +- Is there a way I can make my images smaller? +- What are tags? +- What tags should I use? +- How many tags can I use? +- Why is the "Post" button grayed out? +- How do I format text in Markdown? +- How often can I post? +- How long can my post be? +- If posting in a language other than English, how will I get recognized? +- Can I delete something I posted? +- What does "Promoting" a post do? +- How do I promote a post? + +### Comments +- Can I earn digital tokens for commenting? +- How often can I comment? + +### Economics +- Where do the new STEEM tokens come from? +- How many new tokens are generated by the blockchain? +- How are the new tokens distributed? +- What is the reward pool? +- How is the reward pool split between authors and curators? +- Will the reward pool pay out more or less depending on who votes? +- Why do the earnings for my post go up or down? +- When can I claim my rewards? +- What is the difference between STEEM, STEEM Power, and Steem Dollars? +- What is delegated STEEM Power? +- What determines the price of STEEM? +- How do I get more STEEM Power? +- How long does it take STEEM or STEEM Power that I purchased to show up in my account? +- What is powering up and down? +- What do the dollar amounts for pending payouts represent? +- Will 1 Steem Dollar always be worth $1.00 USD? +- How do Steem Dollar to STEEM conversions work? +- Is there a way for me to convert my Steem Dollars to STEEM without waiting 3.5 days? +- What can I do with my STEEM tokens? +- What can I do with my SBD tokens? +- What is a MVEST? +- Can I sell goods and services on Steemit? +- How can I withdraw my STEEM or SBD coins? +- Will I get a 1099 from Steemit? +- How much are the transaction fees for sending tokens to other users? +- Are there fees for Powering Up, Powering Down, trading on the internal market, or converting SBD to STEEM? +- How long does it take to transfer STEEM or SBD tokens between users? + +### Voting and Curating +- What is my voting power? +- How many times can I vote without depleting my voting power? +- Can I vote with less than 100% of my voting strength? +- Where can I check my voting power? +- What determines how much of the curation reward goes to the author versus curators? +- Can I get curation rewards for upvoting comments? +- Do I get curation rewards for downvoting posts or comments? +- What are curation trails? +- Why don't my upvotes have an effect on a post's rewards? +- Is there a way to make my votes count for more? +- What are the valid reasons for downvoting? +- Does a downvote mean that I did something wrong? +- Will a downvote hurt my reputation? +- What is the difference between a downvote and a flag? + +### Plagiarism, Spam, and Abuse +- What is considered spam or abuse? +- What are Steemit’s policies on plagiarism? +- Is it okay to use random pictures from the internet? +- What is Steemcleaners? +- What is @cheetah? +- Where do I report a post or comment that contains plagiarism, spam, or abuse? + +### Reputation +- What is Reputation? +- How is the Reputation score measured? +- How do I improve my reputation score? +- What causes my reputation score to go down? +- Why does my reputation score matter? + +### Followers, Feeds, and Resteem +- What is Resteeming? +- Can I share on other social media? + +### Blockchain +- What is a blockchain? +- What is the Steem blockchain? +- What is the difference between Steem and Steemit? +- How is Steem different from Bitcoin? +- What is the difference between Proof of Work, Proof of Stake, and Delegated Proof of Stake? +- How often does the Steem blockchain produce a new block? +- Is there a way to see the raw data that is stored in the blockchain? +- Where can I find the information for the official launch of the blockchain? +- Can I mine STEEM? + +### Steemit, Inc. +- Who is the CEO of Steemit? +- Can I invest in Steemit? +- What does Steemit’s development roadmap look like? +- Am I allowed to use the Steemit logo? +- Can I purchase official Steemit merchandise? +- Did Steemit "pre-mine" tokens? +- What is the Steemit Privacy Policy? + +### Security +- How can I keep my Steem account secure? +- Why should I be careful with my master password? +- Why is the master password a long string of gibberish? +- What are my different keys for? +- What do I do if I lost my password/keys? +- Are my STEEM and Steem Dollar tokens insured in the event of a hack or if someone takes over my account? +- What should I do if I discover that someone hacked my account? +- How does the stolen account recovery process work? +- How do I report a security vulnerability? + +### Developers +- Are the Steem blockchain and Steemit.com code open-source? +- Is there a Github page for Steemit.com? +- Is there a Github page for the Steem blockchain? +- What is available for developers interested in Steem and Steemit? +- How do I use cli_wallet? + +### Witnesses +- What are Steem witnesses? +- How can I vote for witnesses? +- How many witnesses can I vote for? + +### Miscellaneous +- What third-party tools are there for Steemit? +- Is there an official Steemit Facebook page? +- Is there an official Steemit Twitter account? +- What is the Steem Whitepaper and what is its purpose? +- Where can I ask for help if my question was not answered here? + +### Disclaimer +- Third Party References and User Links # General - -## What is Steemit.com? +## What is Steemit.com? Steemit is a social network and content rewards platform that makes the crowd the beneficiaries of the attention economy. It does this be rewarding users with STEEM. Steemit has redefined social media by building a living, breathing, and growing social economy; a community where users are getting rewarded for sharing their voice. -^ - -## How does Steemit work? +^ +## How does Steemit work? Steemit is a social media platform that works by having the crowd reward the crowd for their content. It does this thanks to the Steem blockchain and cryptocurrency; Steem is 'minted' daily and distributed to content producers according to the votes they get. -^ - -## How does Steemit differ from other social media websites? +^ + +## How does Steemit differ from other social media websites? Most social media sites extract value from their userbase for the benefit of shareholders alone. Steemit is different, it's a new kind of attention economy. By connecting with the Steem blockchain (which is decentralized and controlled by the crowd), Steemit users receive all the benefits and rewards for their attention. -^ - -## Does it cost anything to post, comment, or vote? +^ +## Does it cost anything to post, comment, or vote? No. It is free to post, comment, and vote on content on Steemit.com. You might even get paid for it! -^ - -## Can I earn digital tokens on Steemit? How? +^ +## Can I earn digital tokens on Steemit? How? You can earn digital tokens on Steemit by: @@ -227,23 +224,20 @@ You can earn digital tokens on Steemit by: **Purchasing** - Users can purchase STEEM or Steem Dollar tokens directly through the Steemit wallet using bitcoin, Ether, or BitShares tokens. They are also available from other markets and exchanges including BlockTrades, Poloniex, Bittrex, Shapeshift.io, and Changelly. STEEM tokens that are powered up to STEEM Power earn a small amount of interest for holding. -^ - -## Where do the tokens come from? +^ +## Where do the tokens come from? The Steem network continually creates digital tokens to reward content creators and curators. Some of the newly-created tokens are transferred to users who add value to Steemit by posting, commenting, and voting on other people's posts. The remainder is distributed to holders of STEEM Power and the witnesses that power the blockchain. -^ - -## Where does the value come from? +^ +## Where does the value come from? At its root, Steem is simply a points system. However, because this points system is blockchain-based, the points can be traded on markets as tokens. People buy and sell these tokens, and many hold in anticipation of increased purchasing power for various Steem-related services. By analogy, Steem is a game system where users compete for attention and rewards by bringing content and adding value to the platform. The rewards people earn are tokens that have market value and are readily tradable. It is similar to how someone playing a video game could obtain a limited item or currency by playing the game. If the currency or items are transferable between users, then they can sell or buy them on game item markets. -^ - -## Why are people getting vastly different rewards? +^ +## Why are people getting vastly different rewards? Steemit is not a "get rich quick" scheme. While it is possible to post content that goes viral quickly and earn a lot of rewards on a single post, this is not typical for most users. @@ -251,11 +245,10 @@ Most of the authors that you see earning high rewards are users that have spent It is best to have realistic expectations, without focusing on rewards when you are first starting out. Work on building a following, making connections, and developing a good reputation. Consistency will pay off in the long run. -^ +^ # Accounts - -## How do I create an account? +## How do I create an account? Click on the "Sign Up" link at the top of Steemit.com to get started. @@ -263,35 +256,30 @@ You will be asked to verify your email address and phone number. After your emai After you receive notification that your account is approved, click on the link in the email to finish the account creation process. Be sure to save and backup your username and password. It is very important that you do not lose your password. There is no way to recover your password or access your account if it is lost. Once your password is saved and backed up, click on the "Create Account" button to create the account. -^ - -## What information do I need to provide in order to create an account? +^ +## What information do I need to provide in order to create an account? You will need to provide your email address and phone number. -^ - -## How long does the account approval process take? +^ +## How long does the account approval process take? Most accounts are approved within 24 hours. Some may take up to a week. If your account has not been approved after one week, please ask for help in the #help channel on steemit.chat. -^ - -## Why do I need to provide my email and phone number? +^ +## Why do I need to provide my email and phone number? To create an account on the blockchain, it costs STEEM tokens. When you create an account through Steemit.com, Steemit Inc. is supplying the tokens to pay the account creation fee. In order to prevent users from abusing the paid-for signup and creating multiple accounts, we need to be able to verify that each user is only signing up for one account. -^ - -## Can I create a Steem account without an email and phone number? +^ +## Can I create a Steem account without an email and phone number? The only way to have an account created via Steemit.com is to supply your email and phone number. Because Steem is an open and permissionless network, there are other ways to create a Steem account. Any Steem blockchain account can be used on Steemit.com -^ - -## What are other ways to create an account on the blockchain besides using Steemit.com? +^ +## What are other ways to create an account on the blockchain besides using Steemit.com? If you are willing to pay your own signup fee, then there are other ways to create a new account on the blockchain. @@ -299,41 +287,35 @@ There is a third-party tool called SteemConnect that allows you to create accounts by paying or delegating the account creation fee. There is no additional fee to use the service, but does require an existing Steem blockchain account to pay the account creation fee to create the account. -^ - -## It is not letting me create an account with my phone number. What should I do? +^ +## It is not letting me create an account with my phone number. What should I do? Ask for help in the #help channel on steemit.chat. -^ - -## What happens if my email or phone number changes? +^ +## What happens if my email or phone number changes? Currently there is no way to change the email or phone number that is linked to your account. Though once your account is created, you can continue to use it even if the email or phone number that is linked to the account has changed. -^ - -## Am I allowed to create more than one account? +^ +## Am I allowed to create more than one account? Each user is allowed only one paid-for account created via Steemit.com, however users are allowed to create multiple accounts on the blockchain. Creating additional accounts on the blockchain requires users to pay their own account creation fee for any additional accounts. -^ - -## Can I delete or deactivate my account? +^ +## Can I delete or deactivate my account? Accounts can not be deactivated or deleted. The account along with all of its activity is permanently stored in the blockchain. -^ +^ # Community - -## Is there an Etiquette Guide for Steemit? +## Is there an Etiquette Guide for Steemit? There are no official rules for participating on Steemit.com, but one of the users @thecryptofiend has created an Etiquette Guide for the community. While it is not required to follow the suggestions in the guide, they are standards that many users in the community choose to follow. -^ - -## Am I required to verify my identity? +^ +## Am I required to verify my identity? Verification is a process where users give evidence to show that they are the person that they claim to be. This is to reduce fraud and people impersonating known figures. If you would like to remain anonymous, that is perfectly fine. However if you claim to be someone specific, the community may expect that you verify you are who you say you are. @@ -341,17 +323,15 @@ There are a number of ways to do this. The most common way to verify your identi Many users also like to post a photo or a video which shows them holding up a sheet of paper with the current date and their Steem account name handwritten on it. This is a great way to add a personal touch to verifying. -^ +^ # Site Navigation - -## How do I upvote a post or comment? +## How do I upvote a post or comment? To upvote a post or comment, click on the "upvote" icon at the bottom of the post/comment. -^ - -## What do the Home, New, Hot, Trending, and Promoted links show? +^ +## What do the Home, New, Hot, Trending, and Promoted links show? These are various ways to sort Steem posts. @@ -365,9 +345,8 @@ These are various ways to sort Steem posts. **Promoted** - Listings that are boosted by Steem Dollar payments get promoted for greater visibility. -^ - -## What information is available in my account menu? +^ +## What information is available in my account menu? You can get to your account menu by clicking on the avatar icon in the top-right corner of a Steemit.com page. @@ -387,9 +366,8 @@ You can get to your account menu by clicking on the avatar icon in the top-right **Logout** - If you'd like to logout. -^ - -## How do I see my recent rewards? +^ +## How do I see my recent rewards? The Rewards drop-down menu is available on your profile/blog page. Click it and there are two links: @@ -399,21 +377,18 @@ The Rewards drop-down menu is available on your profile/blog page. Click it and You can also view the same information for other users by visiting their profile. -^ - -## What information is shown in my wallet? +^ +## What information is shown in my wallet? Your wallet shows how many STEEM and Steem Dollar tokens you have in your account. It shows how much STEEM Power it has, and how much SP is delegated. It also shows how many of your STEEM and Steem Dollar tokens are being held in the savings account, which is a balance that is subject to 3 day withdraw waiting period. The wallet page shows any the progress of any Steem Dollar to STEEM conversions as well as the status of a power down. It also shows an estimated value of all the tokens in your account, based on the recent market prices of STEEM and SBD. -^ - -## How do I transfer my STEEM or Steem Dollars into savings? +^ +## How do I transfer my STEEM or Steem Dollars into savings? Your savings balance is STEEM and SBD tokens that are subject to 3 day withdraw waiting period. This is an extra security measure in case your account credentials are compromised. To transfer STEEM or SBD tokens into savings, click on the drop-down arrow next to STEEM or STEEM DOLLARS in your wallet, and select "Transfer to Savings". -^ - -## How do I send money to another user? +^ +## How do I send money to another user? - From your wallet page, click the STEEM or Steem Dollar balances with the down arrow next to them. - In the drop-down menu, click 'Transfer'. @@ -423,9 +398,8 @@ Your savings balance is STEEM and SBD tokens that are subject to 3 day withdraw - Click Submit. - You will be prompted for your password. You will need to enter your master password or active key. -^ - -## Will I receive notifications when there is activity with my account? +^ +## Will I receive notifications when there is activity with my account? When there is new activity in your feed, you receive a reply from another user, or there is a new transfer in your wallet, you will receive a notification in your account menu. It will show a little red number showing the number of new notifications. @@ -433,9 +407,8 @@ Steemit also allows you to subscribe to receive additional notifications when us Currently, there are no options to receive notifications for votes directly on Steemit.com. But, there is a third-party application https://steemstats.com/, developed by @jesta, which has an option to set up additional notifications on your computer. -^ - -## What is shown in my profile? +^ +## What is shown in my profile? At the top of your profile is your display name and reputation score. Below your display name is the number of followers you have, the number of posts and comments you have written, and the number of people you are following. It also shows the month and year when your account was created. @@ -443,69 +416,59 @@ You have the option to change your avatar and display name on the Settings page. You can view your own profile by clicking on the link to your Blog in your account menu. -^ - -## How do I change my avatar image and other profile information? +^ +## How do I change my avatar image and other profile information? Your profile info, avatar image, and cover image are set in your Settings page. In order to update your avatar picture and cover image, you will need to host the images somewhere. This can be done by uploading it to a Steemit comment or post, or using a third-party image host such as Postimage. Once your image is uploaded, copy its URL and paste it into the "Profile Picture URL" box for the avatar, or the "Cover Image URL" box for the cover image. Then click the Update button and enter your password or active key. -^ - -## What is the recommend size for the cover image? +^ +## What is the recommend size for the cover image? The cover image will be resized/scaled depending on the device being used. Therefore it is recommend to use an image that will still look good when cropped or resized. A 2048x512 image is the optimal size to work for most devices. -^ - -## How can I control whether I see "Not Safe For Work" (NSFW) content? +^ +## How can I control whether I see "Not Safe For Work" (NSFW) content? By default, content that users have tagged as "NSFW" will be hidden, but a link will be shown to reveal the content. You can update your display preference with the Settings page so that NSFW content is always shown by default, remains hidden until clicked, or is completely hidden with no option to reveal. -^ - -## How do I search for content? +^ +## How do I search for content? In the upper right corner of Steemit, there is a magnifying glass search link where you can find posts using a keyword search. There is also an **Explore** link in the main menu, where you can browse through posts based on tags. -^ - -## Can I see which users I have muted? +^ +## Can I see which users I have muted? Yes. This can be seen under the Settings page. -^ - -## Can I see which users have muted me? +^ +## Can I see which users have muted me? No. This information is not presented on Steemit.com. -^ - -## Can I see the list of users I am following, and who is following me? +^ +## Can I see the list of users I am following, and who is following me? Yes. You can see the list of followers or people you are following by clicking on the links on your profile page. -^ - -## What languages are supported? +^ +## What languages are supported? English is the most-used language used on the Steemit platform, but communities are forming that speak other languages. -^ +^ # Posting - -## What can users post to Steemit? +## What can users post to Steemit? Steem is an open platform meant to host and welcome any legal content. Users can post anything they want, whether it be phrases, quotes, blogs, anecdotes, photos, videos, memes, songs, and more. Be creative! -^ - -## What are the different choices for post rewards (50%/50%, Power Up 100%, Decline Payout)? +^ +## What are the different choices for post rewards (50%/50%, Power Up 100%, Decline Payout)? - **50%/50%** - This rewards in half STEEM Power, and half liquid STEEM / Steem Dollars. The ratio of liquid STEEM to Steem Dollars rewarded is based on network conditions at the time of payout. This is the default payout option. @@ -513,9 +476,8 @@ Steem is an open platform meant to host and welcome any legal content. Users can - **Decline Payout** - Use this option to receive no post rewards. Votes will affect the post's position on the trending ranking but no rewards are paid from Steem's reward pool. Replies made to the post are still eligible for rewards. -^ - -## How do I add images and photos to my posts? +^ +## How do I add images and photos to my posts? You can browse your hard drive to add an image by clicking on the "selecting them" link from within the editor. @@ -523,41 +485,35 @@ If you have an image copied to your clipboard, you can simply paste (`ctrl + v`) Pictures can also be hosted on an external site. Paste the image's web address (URL) into the editor and it will automatically be added. -^ - -## How do I set the thumbnail image for my post? +^ +## How do I set the thumbnail image for my post? The first image in the post will automatically be set as the thumbnail image. -^ - -## What is the recommend aspect ratio for thumbnail images? +^ +## What is the recommend aspect ratio for thumbnail images? The recommend aspect ratio for thumbnail images is 16x9. -^ - -## How do I add videos to my posts? +^ +## How do I add videos to my posts? To add a YouTube or Vimeo video to your blog post, simply paste the link to the video into the post. You can also read this guide from @algimantas, which has more detailed instructions: -^ - -## Is there a way I can make my images smaller? +^ +## Is there a way I can make my images smaller? Yes, but the picture must be resized before it is uploaded into the Steemit.com editor. This can be done in your favorite photo editing software, or online by uploading to a third-party website that features editing such as imgur.com. -^ - -## What are tags? +^ +## What are tags? Tags are a way to categorize your content, so that others can find it. The more relevant the tags are to the post, the more like-minded people will come across it. -^ - -## What tags should I use? +^ +## What tags should I use? Try to use tags that are relevant to your post, and that will be popular for other people to browse. For example, "mytriptoalaska" may be relevant to your post, but readers are probably not going to go searching for that. Using "travel" would be a better choice for a tag in this case. @@ -567,21 +523,18 @@ Be mindful when choosing tags. If your tags aren’t related to your post, your All tags must be lowercase letters. Spaces aren't allowed, but hyphenated words with a single dash are. -^ - -## How many tags can I use? +^ +## How many tags can I use? You can use up to 5 tags per post. -^ - -## Why is the "Post" button grayed out? +^ +## Why is the "Post" button grayed out? A post must have a title, body, and at least one valid tag. If any of these are missing, then the "Post" button will be disabled. -^ - -## How do I format text in Markdown? +^ +## How do I format text in Markdown? Some common markdown syntax is: - `**bold**` **bold** @@ -602,21 +555,18 @@ Text can be sized using headers: For more advanced formatting, a guide describing the common markdown formatting syntax can be found here: Markdown Cheatsheet -^ - -## How often can I post? +^ +## How often can I post? You are allowed to post almost as often as you like. Currently, posts must be spaced 5 minutes apart. However, the community may not find value in users that are posting too frequently. Keep in mind what your audience will be interested in viewing, so that you do not overwhelm your followers with too much content. -^ - -## How long can my post be? +^ +## How long can my post be? Post sizes are limited to about 64,000 characters including formatting. This is ample for most posts. If writing blogs, consider how much people are willing to read at one time. If you make your posts too long, readers may lose interest which may affect the amount of upvotes and rewards you receive. -^ - -## If posting in a language other than English, how will I get recognized? +^ +## If posting in a language other than English, how will I get recognized? You can use language-specific tags to help you to reach the audience that speaks your language. @@ -629,15 +579,13 @@ Language-specific groups include: - French = fr - Portuguese = pt -^ - -## Can I delete something I posted? +^ +## Can I delete something I posted? The blockchain will always contain the full edit history of posts and comments, so it can never be completely deleted. If you would like to update a post so that users cannot see the content via Steemit.com, you can edit the post and replace it with blank content for as long as the post is active. After seven days, the post can no longer be edited. -^ - -## What does "Promoting" a post do? +^ +## What does "Promoting" a post do? When you make a post, there is the option to promote it with Steem Dollars. It will then show up in the “Promoted” tab. The order that it appears in the list depends on how much the post was promoted for. Posts with a higher promoted amount will be higher than posts with less. @@ -645,72 +593,62 @@ Steem Dollars spent to promote a post are paid to the account @null, which nobod You can promote your own posts, or posts that you like from other users. -^ - -## How do I promote a post? +^ +## How do I promote a post? At the bottom of each post is a button to "Promote". After clicking the button, type the number of Steem Dollars that you want to spend and click “PROMOTE”. The operation will require your master password or active key. -^ +^ # Comments - -## Can I earn digital tokens for commenting? +## Can I earn digital tokens for commenting? Yes, comments that are upvoted can earn rewards just like posts! -^ - -## How often can I comment? +^ +## How often can I comment? There is a 20 second wait time in between comments to limit spam. -^ +^ # Economics - -## Where do the new STEEM tokens come from? +## Where do the new STEEM tokens come from? Blockchains like Steem and Bitcoin produce new tokens each time a block is produced. Unlike Bitcoin, where all of the new coins go to the block producers (called miners), the Steem blockchain allocates a majority of the new tokens to a reward fund. The reward fund gives users tokens for participating in the platform. -^ - -## How many new tokens are generated by the blockchain? +^ +## How many new tokens are generated by the blockchain? Starting with the network's 16th hard fork in December 2016, Steem began creating new tokens at a yearly inflation rate of 9.5%. The inflation rate decreases at a rate of 0.01% every 250,000 blocks, or about 0.5% per year. The inflation will continue decreasing at this pace until the overall inflation rate reaches 0.95%. This will take about 20.5 years from the time hard fork 16 went into effect. -^ - -## How are the new tokens distributed? +^ +## How are the new tokens distributed? Out of the new tokens that are generated: - 75% go to the reward pool, which is split between authors and curators. - 15% of the new tokens are awarded to holders of STEEM Power. - The remaining 10% pays for the witnesses to power the blockchain. -^ - -## What is the reward pool? +^ +## What is the reward pool? Every day, a fixed amount of STEEM tokens are allocated to the network reward fund, commonly called the "reward pool." These get distributed to authors and curators for posting and voting on content. -^ - -## How is the reward pool split between authors and curators? +^ +## How is the reward pool split between authors and curators? Up to 25% of a post's payout is awarded to curators (the people who upvoted the post) as a reward for discovering the content. The other 75% is awarded to the author. If curators vote for a post within the first 30 minutes of it being created, a portion of their curation reward is added to the author payout. This portion is linear to the age of the post between 0 and 30 minutes. Therefore upvoting at 15 minutes old will donate half of your potential curation reward to the author. -^ - -## Will the reward pool pay out more or less depending on who votes? +^ +## Will the reward pool pay out more or less depending on who votes? There is a fixed amount of STEEM coins that gets added to the rewards pool each day. In the short term, the amount of coins that get paid out may be higher or lower depending on the amount of voting activity, but over time it will pay out the full amount of rewards regardless of who votes. Votes in Steem are stake-weighted. Therefore voters with more STEEM Power have a greater influence over the allocation than voters with less SP, but their votes do not increase the amount of rewards. -^ - -## Why do the earnings for my post go up or down? +^ +## Why do the earnings for my post go up or down? The amount that is shown next to a post is a "**Potential Payout**". This is an estimated value of how much money the post will make based on the votes that have occurred so far. Depending on various factors, this value can go up or down until the payout window closes: @@ -723,15 +661,13 @@ The amount that is shown next to a post is a "**Potential Payout**". This is an - If the price of STEEM goes up, the potential payout of all posts can go up. - If the price of STEEM goes down, the potential payout of all posts can go down. -^ - -## When can I claim my rewards? +^ +## When can I claim my rewards? Posts and comments remain active for 7 days. When the period is over, you are able to claim their earned rewards. In your Wallet, click the Claim Rewards button to add the tokens to your account. -^ - -## What is the difference between STEEM, STEEM Power, and Steem Dollars? +^ +## What is the difference between STEEM, STEEM Power, and Steem Dollars? **STEEM** - STEEM is the base liquid currency token in the platform. STEEM can be powered up into STEEM Power, traded for Steem Dollars, and transferred to other accounts. It is a cryptocurrency token, similar to bitcoin. @@ -739,9 +675,8 @@ Posts and comments remain active for 7 days. When the period is over, you are ab **Steem Dollars** - Steem Dollars (commonly abbreviated SBD) are liquid stable-value currency tokens designed to be pegged to $1 USD. Steem Dollars can be traded with STEEM, and transferred to other accounts for commerce or exchange. Steem Dollars may also be converted into STEEM in a process that takes 3.5 days. Steem Dollars can be used to buy things in marketplaces, such as PeerHub.com. -^ - -## What is delegated STEEM Power? +^ +## What is delegated STEEM Power? Users have the option to delegate STEEM Power to other users. When a user is delegated STEEM Power, their content votes and curation rewards are calculated as if it were their own STEEM Power. Users are not able to power down or cash out delegated STEEM Power, as it still belongs to the original owner. @@ -749,15 +684,13 @@ Most users will have a small amount of STEEM Power delegated to them by the Stee Delegated STEEM Power shows up in a user's wallet below their actual STEEM Power balance in parentheses. -^ - -## What determines the price of STEEM? +^ +## What determines the price of STEEM? The price of STEEM is based on the supply and demand of the token, determined by buyers and sellers on the exchanges. It is similar to how the price of a commodity like gold is determined. -^ - -## How do I get more STEEM Power? +^ +## How do I get more STEEM Power? With STEEM tokens in your wallet, click "Power Up" to turn them into STEEM Power. If you have Steem Dollars, you can convert them to STEEM from your wallet, and then power up the STEEM. @@ -774,25 +707,22 @@ STEEM purchases made via Steemit.com are facilitated by Poloniex, Bittrex, ShapeShift.io, and Changelly. -^ - -## How long does it take STEEM or STEEM Power that I purchased to show up in my account? +^ +## How long does it take STEEM or STEEM Power that I purchased to show up in my account? Transactions on the Steem blockchain typically only take about three seconds to process, but when you are purchasing the STEEM tokens using bitcoin or some other token, then the transaction must wait for the transaction to be confirmed on the other network. This can often take several hours, and sometimes even days. If you paid using bitcoin, the third party website bitcoinfees.21.co can estimate the approximate wait time of the transaction based on the fees that were paid. The third party website blockchain.info will lookup the fees that were paid on a specific blockchain transaction. -^ - -## What is powering up and down? +^ +## What is powering up and down? **Powering up** - If you have STEEM tokens, you can Power Up to STEEM Power to get more voting influence on posts and comments. Having more STEEM Power also increases the amount of curation rewards and interest that you can earn. More SP also grants more influence on approving Steem witnesses. **Powering down** - If you have STEEM Power, you can power down to turn it into liquid STEEM over a period of time. The system will transfer 1/13 of your STEEM Power to STEEM each week for about three months (13 weeks), starting 1 week from the time it is started. However, you will lose your influence in the network proportionally to how much is powered down, so think about it carefully. Power downs can be stopped at any time. -^ - -## What do the dollar amounts for pending payouts represent? +^ +## What do the dollar amounts for pending payouts represent? The dollar amounts next to posts and comments are estimates of the potential payout that will occur when the payout period ends, based on the current voting activity and price of STEEM. These potential payout amounts may fluctuate up or down until the payout period ends. @@ -800,38 +730,33 @@ Payouts occur as a combination of STEEM Power and Steem Dollars. Sometimes the b The blockchain estimates the dollar value of STEEM and STEEM Power based on the 3.5 day average price of STEEM reported by the witnesses. The blockchain assumes Steem Dollars are worth approximately one USD. -^ - -## Will 1 Steem Dollar always be worth $1.00 USD? +^ +## Will 1 Steem Dollar always be worth $1.00 USD? The market value of a Steem Dollar is dictated by the supply and demand of the token. Therefore it is possible for 1 SBD to be worth more or less than 1 USD depending on market conditions. However, the network's SBD conversion feature serves as a mechanism to hold Steem Dollars within a small margin of the value of USD. -^ - -## How do Steem Dollar to STEEM conversions work? +^ +## How do Steem Dollar to STEEM conversions work? If you convert Steem Dollars to STEEM on the Wallet page, the blockchain will process the transaction over a period of 3.5 days. At the end of the 3.5 days, the SBD will be gone and replaced by approximately $1 USD worth of STEEM tokens. The "approximately 1 USD worth of STEEM tokens" is based on the median STEEM price over the 3.5 days, using the price feeds from the Steem witnesses. Depending on price fluctuations during the 3.5 days it is possible to end up with more or less than $1 USD worth of STEEM per SBD at the end of the conversion. -^ - -## Is there a way for me to convert my Steem Dollars to STEEM without waiting 3.5 days? +^ +## Is there a way for me to convert my Steem Dollars to STEEM without waiting 3.5 days? You can exchange them. Visit the internal Market, found in the main menu. There you can exchange your SBD for STEEM in real-time at whatever the current market price is. Depending on market conditions, users may get more STEEM for their SBD by trading them for STEEM on the internal market, rather than using the conversion. -^ - -## What can I do with my STEEM tokens? +^ +## What can I do with my STEEM tokens? - "Power Up" to STEEM Power - Exchange for SBD in the internal market - Withdraw to an exchange, and trade for BTC or other digital tokens - Purchase items through third-party stores that accept STEEM tokens -^ - -## What can I do with my SBD tokens? +^ +## What can I do with my SBD tokens? - Hold them as a stable-value token - Convert to STEEM via your wallet (takes 3.5 days) @@ -839,25 +764,22 @@ Depending on market conditions, users may get more STEEM for their SBD by tradin - Withdraw to an exchange, and trade for BTC or other digital tokens - Purchase items through third-party stores that accept SBD tokens -^ - -## What is a MVEST? +^ +## What is a MVEST? A VEST is a unit of measurement for STEEM Power. A MVEST is one million VESTS. The amount of STEEM Power in one MVEST can be found on steemd.com as `steem_per_mvests`. -^ - -## Can I sell goods and services on Steemit? +^ +## Can I sell goods and services on Steemit? Other than making a post and making sales manually, there is no interface for selling items directly on Steemit.com. You can list goods on the third-party website PeerHub.com. Through PeerHub, you can accept payment in Steem Dollars or STEEM, and you have the option to advertise your items through Steemit posts. -^ - -## How can I withdraw my STEEM or SBD coins? +^ +## How can I withdraw my STEEM or SBD coins? STEEM and SBD tokens are readily tradable to bitcoin, which is readily tradable to the local currency of your choice. There is a link to "Sell" your STEEM and SBD tokens in your wallet, which uses the BlockTrades interface. -There are several guides that have been posted by users in the community for using various external exchanges to withdraw STEEM and SBD tokens. Please read the disclaimer before using any of these guides to withdraw your coins. The users, guides, and exchanges listed in the guides are not endorsed by Steemit, Inc. Use the guides below at your own risk. +There are several guides that have been posted by users in the community for using various external exchanges to withdraw STEEM and SBD tokens. Please read the disclaimer before using any of these guides to withdraw your coins. The users, guides, and exchanges listed in the guides are not endorsed by Steemit, Inc. Use the guides below at your own risk. It is recommended that you withdraw a small amount first, to verify it works before withdrawing a larger amount. @@ -873,49 +795,42 @@ https://steemit.com/tutorial/@beanz/how-to-get-my-usdteemit-money-into-my-bank-a #### Convert STEEM to many other cryptocurrencies via ShapeShift https://steemit.com/steemit/@shapeshiftio/official-announcement-shapeshift-has-added-steem-to-the-exchange -^ - -## Will I get a 1099 from Steemit? +^ +## Will I get a 1099 from Steemit? No, you are not being paid by Steemit. The Steem network rewards you. It is your responsibility to determine what, if any, taxes apply to the transactions you make. Further, it is your responsibility to report and remit the correct tax to the appropriate tax authority. By creating an account, you agree that Steemit Inc is not responsible for determining whether taxes apply to your Steem transactions or for collecting, reporting, withholding, or remitting any taxes arising from any Steem transactions. -^ - -## How much are the transaction fees for sending tokens to other users? +^ +## How much are the transaction fees for sending tokens to other users? There are never any fees for transfers within the Steem network. However, if you transfer Steem to an exchange and convert it to another currency, you will incur a small fee from the exchange. -^ - -## Are there fees for Powering Up, Powering Down, trading on the internal market, or converting SBD to STEEM? +^ +## Are there fees for Powering Up, Powering Down, trading on the internal market, or converting SBD to STEEM? No. None of these actions incur any fees. -^ - -## How long does it take to transfer STEEM or SBD tokens between users? +^ +## How long does it take to transfer STEEM or SBD tokens between users? A transfer of tokens between accounts typically takes 3 seconds. This is far faster than most blockchain tokens. -^ +^ # Voting and Curating - -## What is my voting power? +## What is my voting power? Voting power is like an "energy bar" in a computer game that goes down a little bit every time you vote. You start out with 100% voting power. Every time you vote, you will use a small amount of your voting power. As you use more of your voting power, your votes will carry less influence. A vote with 50% voting power left will be worth 1/2 as much as a vote cast with 100% voting power. Not to worry, the network recharges your voting power by 20% every day. -^ - -## How many times can I vote without depleting my voting power? +^ +## How many times can I vote without depleting my voting power? Every 100% vote you cast will use 2% of your remaining voting power. Your voting power will recharge by 20% each day. You can vote more than 10 times per day, but each vote will be worth less, and it will take longer to reach full voting power again. -^ - -## Can I vote with less than 100% of my voting strength? +^ +## Can I vote with less than 100% of my voting strength? New users can only upvote and downvote with 100% voting strength. @@ -925,15 +840,13 @@ Once you reach about 500 STEEM Power, you will see a vote slider appear when you Upvotes and downvotes use the same amount of voting power. -^ - -## Where can I check my voting power? +^ +## Where can I check my voting power? You can view your current voting power using third party tools such as https://steemd.com/@youraccount or https://steemstats.com. -^ - -## What determines how much of the curation reward goes to the author versus curators? +^ +## What determines how much of the curation reward goes to the author versus curators? The rewards are allocated so that 75% of the payout goes to the author of the post/comment, and 25% goes to the curator. @@ -945,69 +858,59 @@ Of the 25% that goes to the curator, that portion will be split between the auth - At 27 minutes, 10% goes to the author and 90% to the curator. - If a post is upvoted 30 min after posting, 100% of the curation reward goes to the curator. -^ - -## Can I get curation rewards for upvoting comments? +^ +## Can I get curation rewards for upvoting comments? Yes. You can earn curation rewards from upvoting both posts and comments! -^ - -## Do I get curation rewards for downvoting posts or comments? +^ +## Do I get curation rewards for downvoting posts or comments? No. Since downvoting reduces the rewards on a post/comment, it does not earn curation rewards. -^ - -## What are curation trails? +^ +## What are curation trails? Some users decide to use third party applications such as Streemian to automatically cast votes. Users can automatically vote for the same posts and comments that other users does. Typically they will set this up to follow the votes of users who are good at curating. When a user has other users automatically voting for the same content that they do, the people that automatically vote after them are called their "curation trail". -^ - -## Why don't my upvotes have an effect on a post's rewards? +^ +## Why don't my upvotes have an effect on a post's rewards? A user with more SP is going to have a larger influence on the rewards than users with less SP. One vote from a user with a lot of SP can often have more of an effect than 100 votes from users with a small amount of SP. Even though your vote may not have an immediate effect, when it gets added in along with all the other votes at the end of the payout period, it can still affect the payout. It may also cause more users to vote on the post too, because they saw that you upvoted it - so your votes can have an indirect effect on the payout this way. -^ - -## Is there a way to make my votes count for more? +^ +## Is there a way to make my votes count for more? Yes. The more STEEM Power you have, the more influence your votes will have. The platform does not require that anybody purchase SP in order to participate, and there are many users who have earned a lot of STEEM Power without spending any of their own money. You have the option of purchasing more STEEM Power through your Steemit wallet. -^ - -## What are the valid reasons for downvoting? +^ +## What are the valid reasons for downvoting? Users are allowed to downvote for any reason that they want. There are many users in the community who recommend only using the downvote on posts that are abusive. It is up to you if you want to follow this etiquette. -^ - -## Does a downvote mean that I did something wrong? +^ +## Does a downvote mean that I did something wrong? Just because you received a downvote does not mean that you did something wrong. The downvoting person may have just been voting to reallocate the rewards in a way that they felt was more beneficial to the other active posts in the platform. Often users will leave a comment explaining why they downvoted, but sometimes they might not. If they left a reason, it is up to you to determine if you did anything wrong, and if there is anything you want to change. -^ - -## Will a downvote hurt my reputation? +^ +## Will a downvote hurt my reputation? -Not necessarily. See: What causes my reputation score to go down? +Not necessarily. See: What causes my reputation score to go down? -^ - -## What is the difference between a downvote and a flag? +^ +## What is the difference between a downvote and a flag? With the current implementation, there is no difference between a downvote and a flag. They are treated the same at the blockchain level. -^ +^ # Plagiarism, Spam, and Abuse - -## What is considered spam or abuse? +## What is considered spam or abuse? - Asking for money, views, upvotes, follows, or resteems. - Leaving nearly identical or materially similar comments on multiple posts. @@ -1023,15 +926,13 @@ With the current implementation, there is no difference between a downvote and a - Selling or offering to buy votes/resteems/follows, or schemes that facilitate this. - Scams or Fraudulent offers. -^ - -## What are Steemit’s policies on plagiarism? +^ +## What are Steemit’s policies on plagiarism? If you are posting plagiarized or copied content, you can get in legal trouble for violating copyright laws. Plagiarized posts and spam are seen as abuse and will be downvoted by community members. If you are posting or using someone else’s content, you must ensure that you have the rights to use the content, and properly reference the sources where you got the material from. -^ - -## Is it okay to use random pictures from the internet? +^ +## Is it okay to use random pictures from the internet? If you are using an image that is not your own, make sure you are allowed to use the image, and cite the source of the image. @@ -1040,16 +941,14 @@ Using random pictures from the internet without giving credit is discouraged. Yo Here is a post from @mindover that has links to many websites that have images you can use: https://steemit.com/steem-help/@mindover/don-t-plagiarize-images-here-are-13-free-and-legal-ways-to-find-high-quality-photos-you-can-use-on-steemit -^ - -## What is Steemcleaners? +^ +## What is Steemcleaners? Steemcleaners are a group of Steemians concerned with plagiarism, copy/paste, spam, scams and other forms of abuse on Steemit. https://steemit.com/steemcleaners/@steemcleaners/announcing-steemcleaners-the-steemit-abuse-fighting-team -^ - -## What is @cheetah? +^ +## What is @cheetah? @cheetah is a bot developed by @anyx that scours Steemit for copy/pasted content. Cheetah will not downvote copied content, but it alerts other users to look into it further. @@ -1058,17 +957,15 @@ Abusive accounts (serial plagiarists or identity thieves, for example) will go o More information on the @cheetah bot can be found in this post: https://steemit.com/steemit/@cheetah/faq-about-cheetah -^ - -## Where do I report a post or comment that contains plagiarism, spam, or abuse? +^ +## Where do I report a post or comment that contains plagiarism, spam, or abuse? You can report any abusive content to the #steemitabuse channel on steemit.chat. -^ +^ # Reputation - -## What is Reputation? +## What is Reputation? Every user has a reputation score next to their name. The reputation score is one way Steemit measures the amount of value you have brought to the community. It is also a mechanism that is designed to help reduce abuse of the Steemit platform. @@ -1076,9 +973,8 @@ Your reputation goes up when accounts vote on your content. Getting downvoted by Users with a lower reputation score are unable to affect your reputation. -^ - -## How is the Reputation score measured? +^ +## How is the Reputation score measured? Every new user starts off with a reputation score of 25. @@ -1087,24 +983,21 @@ The reputation score is based off of a `log10` system, which means that a score More information about the calculation of the reputation score can be found in this post from @digitalnotvir: https://steemit.com/steemit/@digitalnotvir/how-reputation-scores-are-calculated-the-details-explained-with-simple-math -^ - -## How do I improve my reputation score? +^ +## How do I improve my reputation score? Every time another user upvotes one of your posts or comments, it increases your reputation score. Users with a higher reputation than you will have more of a positive effect. The more STEEM Power that the voter has, the larger the effect is as well. The best way to earn upvotes is by adding value to the Steemit community. -^ - -## What causes my reputation score to go down? +^ +## What causes my reputation score to go down? The only way for your reputation score to go down is to be downvoted by another user. Not all downvotes will cause a reputation loss though. - Downvotes from users with a lower reputation score than you will not hurt your score. - If your post or comment that was downvoted still received more upvotes than downvotes (weighted by SP), then the net effect on your reputation score will still be positive. -^ - -## Why does my reputation score matter? +^ +## Why does my reputation score matter? A reputation score is one way Steemit measures the amount of value you have brought to the community. In real estate, they say there are three variables of the utmost importance: location, location, location. On Steemit, those things are: reputation, reputation, reputation. It’s not to say other variables aren’t important, but reputation will be an enormous factor in your level of success. @@ -1112,53 +1005,46 @@ Many Steemians glance at users’ reputation scores when deciding which articles It is worth noting that if your reputation score goes below 0, Steemit will hide your posts and comments making it very difficult to gain monetary rewards and followers. This incentivizes online etiquette and respect for your fellow Steemians. -^ +^ # Followers, Feeds, and Resteem - -## What is Resteeming? +## What is Resteeming? This is like reblogging or sharing posts on other platforms. Once you resteem a post it will appear in your feed and in your followers' feeds as if you had posted it yourself. Use it conservatively and with caution. It is great to want to share content you like and appreciate with people you follow, but you don't want to overwhelm your followers either. -^ - -## Can I share on other social media? +^ +## Can I share on other social media? Yes you can use the share button to share on Facebook, Twitter or LinkedIn. You are welcome to post your Steemit links on other websites and social media sites. -^ +^ # Blockchain - -## What is a blockchain? +## What is a blockchain? A blockchain is a public ledger of all transactions ever executed. All of the transactions and data are stored in a distributed database. Each time the database is updated, all of updates are done together in a batch called a 'block'. Each time a new block is produced/added, it is appended on to all of the previous blocks - hence the name "blockchain". -^ - -## What is the Steem blockchain? +^ +## What is the Steem blockchain? The Steem blockchain is the publicly accessible distributed database, which records all posts and votes, and distributes the rewards across the network. It is where all of the text content and voting data is stored, and it is where all of the reward calculations and payouts are performed. -^ - -## What is the difference between Steem and Steemit? +^ +## What is the difference between Steem and Steemit? Steem is the name of the blockchain that stores all of the data and transactions, and processes all of the events that take place. STEEM is also a name for the system’s value token (currency). Steemit is a front end web interface to interact with the blockchain, and view the blockchain data. -^ - -## How is Steem different from Bitcoin? +^ +## How is Steem different from Bitcoin? On a technical level, the two networks rely on the same model of a blockchain, but are built upon different technologies and codebase. Steem is based on a new state-of-the-art blockchain technology called Graphene, which uses "witnesses" instead of "miners" to produce blocks. The "delegated proof of stake" model of using witnesses instead of miners allows for greater efficiency in block production. With BTC, 100% of the new coins that are created are allocated to block producers (miners). With the Steem blockchain, only 10% of the new coins are paid to block producers (witnesses). The other 90% of new STEEM coins are awarded to content producers, curators, and STEEM Power holders. -^ - -## What is the difference between Proof of Work, Proof of Stake, and Delegated Proof of Stake? +^ +## What is the difference between Proof of Work, Proof of Stake, and Delegated Proof of Stake? **Proof of work** - Miners solve a complex mathematical problem. The miner that solves the problem first adds the block to the blockchain. The network rewards the miner for doing so. @@ -1166,83 +1052,71 @@ The "delegated proof of stake" model of using witnesses instead of miners allows **Delegated proof of stake** - Block-creating accounts, called witnesses, are collectively approved by Steem stakeholders. Instead of relying on proof of work to find blocks, the Steem network actively schedules these accounts to improve the time between blocks to 3 seconds. -^ - -## How often does the Steem blockchain produce a new block? +^ +## How often does the Steem blockchain produce a new block? The Steem blockchain schedules witnesses to produce a new block every 3 seconds. 21 witness nodes produce 21 blocks in each 63-second round. -^ - -## Is there a way to see the raw data that is stored in the blockchain? +^ +## Is there a way to see the raw data that is stored in the blockchain? Yes. The blockchain data can be viewed in different ways with third-party tools such as steemd.com and steemdb.com. -^ - -## Where can I find the information for the official launch of the blockchain? +^ +## Where can I find the information for the official launch of the blockchain? The original launch of Steem was on March 23, 2016, announced on Bitcointalk.org. There was a bug found in the original code though, and a majority of the stakeholders agreed that it would be easier to fix via a re-launch than a hardfork. The blockchain was reset and officially re-launched on March 24, 2016, via Bitcointalk.org. -^ - -## Can I mine STEEM? +^ +## Can I mine STEEM? No. Proof of work mining has been removed from Steem. -^ +^ # Steemit, Inc. - -## Who is the CEO of Steemit? +## Who is the CEO of Steemit? Ned Scott, @ned https://www.linkedin.com/in/nedscott -^ - -## Can I invest in Steemit? +^ +## Can I invest in Steemit? Steemit, Inc. is a privately held company and is not available for public investment. Though not considered an investment, you can purchase STEEM tokens which can go up or down in value. You can power up these tokens into STEEM Power, which grants more influence in the Steem platform. -^ - -## What does Steemit’s development roadmap look like? +^ +## What does Steemit’s development roadmap look like? You can view the 2017 Roadmap here: https://steemit.com/steemit/@steemitblog/steemit-2017-roadmap -^ - -## Am I allowed to use the Steemit logo? +^ +## Currently, the Steem and Steemit logos are the same and is free to use. In the future, Steemit, Inc. will have its own logo so that it can be distinguished from Steem. The Steemit logo will be proprietary while Steem and its three S-shaped squiggles will remain open for public use. -^ - -## Can I purchase official Steemit merchandise? +^ +## Can I purchase official Steemit merchandise? Yes. Official Steemit merchandise can be purchased from [The Steemit Shop](https://thesteemitshop.com/). -^ - -## Did Steemit "pre-mine" tokens? +^ +## Did Steemit "pre-mine" tokens? The STEEM tokens mined by Steemit, Inc. were not "pre-mined". All mining took place after the coin was officially and publicly announced on Bitcointalk.org. -^ - -## What is the Steemit Privacy Policy? +^ +## What is the Steemit Privacy Policy? https://steemit.com/privacy.html -^ +^ # Security - -## How can I keep my Steem account secure? +## How can I keep my Steem account secure? Save your master password and keep it somewhere safe. @@ -1255,21 +1129,18 @@ Again, save your master password and keep it safe! If logging in with you It is not recommended to share your password or keys with any third party site. Steemit Inc. is developing a login application that can be used on third party Steem front ends. -^ - -## Why should I be careful with my master password? +^ +## Why should I be careful with my master password? The master password is used to derive all keys for your account, including the owner key. -^ - -## Why is the master password a long string of gibberish? +^ +## Why is the master password a long string of gibberish? The password has to be long and random for maximum account security. -^ - -## What are my different keys for? +^ +## What are my different keys for? **Posting key** - The posting key allows accounts to post, comment, edit, vote, resteem, and follow or mute other accounts. Most users should be logging into Steemit every day with the posting key. You are more likely to have your password or key compromised the more you use it so a limited posting key exists to restrict the damage that a compromised account key would cause. @@ -1279,29 +1150,25 @@ The password has to be long and random for maximum account security. **Owner key** - The owner key is only meant for use when necessary. It is the most powerful key because it can change any key of an account, including the owner key. Ideally it is meant to be stored offline, and only used to recover a compromised account. -^ - -## What do I do if I lost my password/keys? +^ +## What do I do if I lost my password/keys? There is no way to recover your account if you lose your password or owner key! Because your account has real value, it is **very important** that you save your master password somewhere safe where you will not lose it. It is strongly recommended that you store an offline copy of your password somewhere safe in case of a hard drive failure or other calamity. Consider digital offline storage, such as an external disk or flash drive, as well as printed paper. Use a safe deposit box for best redundancy. -^ - -## Are my STEEM and Steem Dollar tokens insured in the event of a hack or if someone takes over my account? +^ +## Are my STEEM and Steem Dollar tokens insured in the event of a hack or if someone takes over my account? No, liquid tokens can not be taken back if stolen or sent to the wrong account. If your tokens are in STEEM Power, it is impossible for a hacker to take out more than 1/13 per week. If your tokens are in savings, there is a three-day wait period for them to become transferable. -^ - -## What should I do if I discover that someone hacked my account? +^ +## What should I do if I discover that someone hacked my account? If you made your account through Steemit and it is compromised, immediately visit the Stolen Account Recovery page. This link is also available in the main site menu. You will need to provide the email address that you used when you signed up, your account name, and a master password that was used in the last 30 days. -^ - -## How does the stolen account recovery process work? +^ +## How does the stolen account recovery process work? If your password has been changed without your consent, then the account designated as your recovery account can generate a new owner key for the account. The account recovery must be completed within 30 days of the password being changed, and you must supply a recent owner key that was valid within the last 30 days. @@ -1309,95 +1176,82 @@ Steemit Inc. owns the default recovery account (@steem) for all users who sign u If you don't have the master password or owner key that was valid the past 30 days, or are unable to prove that you are the original owner of the account, then your account will be unrecoverable. -^ - -## How do I report a security vulnerability? +^ +## How do I report a security vulnerability? If you find a security issue please report the details to security@steemit.com. -^ +^ # Developers - -## Are the Steem blockchain and Steemit.com code open-source? +## Are the Steem blockchain and Steemit.com code open-source? Yes. Both the Steem blockchain and Steemit.com are open-source projects. Developers should however avoid the use of the term "Steemit" in their own products, and instead refer to the Steem Blockchain or Steem Platform. Steemit refers to Steemit.com, which is owned by Steemit, Inc. -^ - -## Is there a Github page for Steemit.com? +^ +## Is there a Github page for Steemit.com? https://github.com/steemit/condenser -^ - -## Is there a Github page for the Steem blockchain? +^ +## Is there a Github page for the Steem blockchain? https://github.com/steemit/steem -^ - -## What is available for developers interested in Steem and Steemit? +^ +## What is available for developers interested in Steem and Steemit? Many software engineers are currently leveraging the open-source code to build their applications on Steem. There are more than sixty so far. This post from the user @fabien has more information about the Steem API: https://steemit.com/steemjs/@fabien/steem-api-now-released -^ - -## How do I use cli_wallet? +^ +## How do I use cli_wallet? Here is a guide from the user @pfunk explaining how to use the cli_wallet: https://steemit.com/steemhelp/@pfunk/a-learner-s-guide-to-using-steem-s-cliwallet-part-1 -^ +^ # Witnesses - -## What are Steem witnesses? +## What are Steem witnesses? The Steem blockchain requires a set of people to create blocks and uses a consensus mechanism called delegated proof of stake, or DPOS. The community elects 'witnesses' to act as the network's block producers and governance body. There are 20 full-time witnesses, producing a block every 63-second round. A 21st position is shared by backup witnesses, who are scheduled proportionally to the amount of stake-weighted community approval they have. Witnesses are compensated with STEEM Power for each block they create. Steemit leverages Steem because the founders of Steemit believe Steem’s decentralized text content storage and governance model makes Steem an excellent platform for supporting the long term success of its social network and digital currency tokens. -^ - -## How can I vote for witnesses? +^ +## How can I vote for witnesses? Visit https://steemit.com/~witnesses -^ - -## How many witnesses can I vote for? +^ +## How many witnesses can I vote for? Each account can vote for up to 30 witnesses. -^ +^ # Miscellaneous - -## What third-party tools are there for Steemit? +## What third-party tools are there for Steemit? http://steemtools.com/ -^ - -## Is there an official Steemit Facebook page? +^ +## Is there an official Steemit Facebook page? https://www.facebook.com/steemit/ -^ - -## Is there an official Steemit Twitter account? +^ +## Is there an official Steemit Twitter account? https://twitter.com/steemit -^ - -## What is the Steem Whitepaper and what is its purpose? +^ +## What is the Steem Whitepaper and what is its purpose? The Steem Whitepaper was written to describe the mechanics of the token system that makes decentralized content incentives and distribution possible in a way that can improve web technologies across the board. It is also applicable to Steemit, the first website to plug into the Steem blockchain. Users who have read the Steem Whitepaper will better understand how their interactions with Steemit are interactions with Steem, the decentralized network. @@ -1405,20 +1259,18 @@ It is worth noting that the Whitepaper hasn’t been updated almost since Steem https://steem.io/SteemWhitePaper.pdf -^ - -## Where can I ask for help if my question was not answered here? +^ +## Where can I ask for help if my question was not answered here? If you post your question in the #help channel on steemit.chat, the users there may be able to help. You can also create a post on Steemit.com with the tag #help, and someone in the community may be able to answer it. -^ - -# Disclaimer +^ + +# Disclaimer - -## Third Party References and User Links +## Third Party References and User Links BlockTrades, Poloniex, Bittrex, Changelly, Shapeshift.io, Coinbase, Localbitcoins, SteemDB, PeerHub, Steemit.chat, SteemTools, AnonSteem, SteemConnect, Streemian, SteemStats, Pixabay, Steemcleaners, Pexels, Postimage, Markdown Cheatsheet, @cheetah, Bitcointalk, bitcoinfees, blockchain.info, and steemd are third party applications/services, and are not owned or maintained by Steemit, Inc. Their listing here, as well as any other third party applications or websites that are listed, does not constitute and endorsement or recommendation on behalf of Steemit, Inc. @@ -1426,4 +1278,4 @@ All links to user posts were created by our users and do not necessarily represe Please use the third party tools and content at your own risk. -^ +^ diff --git a/src/app/help/en/welcome.md b/src/app/help/en/welcome.md index ea19b4eed..4c74c9887 100644 --- a/src/app/help/en/welcome.md +++ b/src/app/help/en/welcome.md @@ -1,3 +1,4 @@ + ## Welcome to Steemit! This page is full of information to help you learn about the platform and become a successful Steemian. You can return to this page at any time by clicking on the "Welcome" link in the main menu. There is a table of contents below to help you navigate the page. @@ -8,70 +9,65 @@ Below that is a section of "Helpful Posts from Steemit Users", which contains a Below that is a list of recommended users to follow, a collection of other resources including the FAQ Page, and information on where to find live help. -## Table of Contents - +## Table of Contents ### Quick Start Guide -- No Cost to Participate -- Upvotes -- Comments -- Creating Posts -- Tags -- Followers and Feeds -- Resteem -- Digital Currencies -- Curation -- Payments -- Home, New, Hot, Trending, Promoted, and Active -- Profile -- Reputation -- Cashing out or Spending SBD -- Plagiarism -- Password Security -- Earning on Steemit +- No Cost to Participate +- Upvotes +- Comments +- Creating Posts +- Tags +- Followers and Feeds +- Resteem +- Digital Currencies +- Curation +- Payments +- Home, New, Hot, Trending, Promoted, and Active +- Profile +- Reputation +- Cashing out or Spending SBD +- Plagiarism +- Password Security +- Earning on Steemit ### To Do List -1. Backup your password -2. Sign Up for Steemit Chat -3. Setup your Profile, Avatar, and Cover Image -4. Choose your "NSFW" (Not Safe for Work) Display Preference -5. Create your "introduceyourself" post +1. Backup your password +2. Sign Up for Steemit Chat +3. Setup your Profile, Avatar, and Cover Image +4. Choose your "NSFW" (Not Safe for Work) Display Preference +5. Create your "introduceyourself" post -### Helpful Posts from Steemit Users -### Users to Follow -### Other Resources -### Live Help -### Third Party References +### Helpful Posts from Steemit Users +### Users to Follow +### Other Resources +### Live Help +### Third Party References *** ## Quick Start Guide - -### No Cost to Participate +### No Cost to Participate It is free to post, comment, or upvote all content on Steemit.com. You might even get paid for it! -^ - -### Upvotes +^ +### Upvotes Upvotes are Steemit's way of saying you like someone's post or comment. To upvote, click on the *Upvote* icon at the bottom of the comment/post. -^ - -### Comments +^ +### Comments When you are first starting out, commenting on other people's posts can be a great way to get involved and connect with people! To comment on a post, or reply to an existing comment, click on the "Reply" link at the bottom of the post/comment. -^ - -### Creating Posts +^ +### Creating Posts To create a post, click on the "Post" link in the upper right corner. @@ -83,9 +79,8 @@ To create your content, you can either use "Editor" or "Markdown" mode. There are several guides for creating posts in the "Helpful Posts from Steemit Users" section below. -^ - -### Tags +^ +### Tags Tags will help people find your posts. @@ -97,9 +92,8 @@ The tags should all be relevant to the content in the post. You can browse content by tags, as well as see a list of popular tags that other users have used in their posts [here](https://steemit.com/tags). -^ - -### Followers and Feeds +^ +### Followers and Feeds To follow an author, click on their username and click the "Follow" button. @@ -109,31 +103,27 @@ As other Steemians come across your posts and comments, you will start to gain f You can see all of your followers and the people you are following in your profile page. -^ - -### Resteem +^ +### Resteem If you want to share someone else's post with all of your followers, click on the *resteem* icon. -^ - -### Digital Currencies +^ +### Digital Currencies STEEM, Steem Power and Steem Dollars are the three forms of digital currency used by the Steem Blockchain. More information on the three types of tokens can be found in the [Steemit FAQ](https://steemit.com/faq.html). -^ - -### Curation +^ +### Curation Up to 25% of the reward for posts goes to the people who voted on it. These people are called curators. The more Steem Power you have in your account, the more your upvotes will be worth, and the more potential curation rewards you can earn! -^ - -### Payments +^ +### Payments Payouts are made 7 days after the post/comment is created. You can claim your rewards in your wallet after 7 days. @@ -145,9 +135,8 @@ The author reward is paid 50% in Steem Power, and 50% in liquid STEEM/SBD. Authors also have the option to decline payout, or be paid in 100% Steem Power! -^ - -### Home, New, Hot, Trending, Promoted, and Active +^ +### Home, New, Hot, Trending, Promoted, and Active These are various ways to sort blog posts. @@ -161,9 +150,8 @@ These are various ways to sort blog posts. **Promoted** - Listings that are boosted by Steem Dollar payments get "Promoted" for greater visibility. -^ - -### Profile +^ +### Profile @@ -183,9 +171,8 @@ These are various ways to sort blog posts. **Logout** - Here is where you go to logout. -^ - -### Reputation +^ +### Reputation A reputation score is one way Steemit measures the amount of value you have brought to the community. @@ -195,9 +182,8 @@ All new users start at 25. Your reputation will go up as you earn upvotes for your posts and comments, but it can come down if they are flagged. -^ - -### Cashing out or Spending SBD +^ +### Cashing out or Spending SBD You can spend your SBD at the [Peerhub Store](https://www.peerhub.com/). @@ -205,9 +191,8 @@ You can exchange your STEEM and SBD for bitcoin on an exchange such as [BlockTra You can also "Power Up" and use your STEEM/SBD to gain more Steem Power! -^ - -### Plagiarism +^ +### Plagiarism The community is looking for you to add your own personal touch to your articles. @@ -217,30 +202,27 @@ If you are using anyone else's material as part of your posts (including images) Also, make sure that you are not violating any copyright laws if you are using someone else's material/images. Limited, sourced material sharing is OK under fair use and fair dealing doctrines. -^ - -### Password Security +^ +### Password Security Your Steemit account is worth real money. Treat your Steemit password like you would your bank password, and keep it secure! Unless your password was recently changed and you possess the old one, **there is no password recovery for Steem accounts**. You are 100% responsible for having it backed up. This means secure digital backups, as well as secured paper backups, off-site if possible. -^ - -### Earning on Steemit +^ +### Earning on Steemit The best attitude to have is to expect to make nothing. Have fun. Get engaged. Make friends. If along the way you earn something - bonus! It is possible to earn thousands of dollars, but most authors who are doing this have put in a lot of time and work to contribute to the community and build followings. -^ +^ *** ## To Do List - - -### 1. Backup your password + +### 1. Backup your password Unlike centralized web services, **the Steem Blockchain has no account password recovery**. @@ -252,9 +234,8 @@ It is strongly recommended that you store an offline copy of your password somew If your account is valuable, treat it like a valuable! -^ - -### 2. Sign Up for Steemit Chat +^ +### 2. Sign Up for Steemit Chat A lot of users hang out and chat when they are not posting or browsing Steemit. It is a great place to meet people! @@ -266,9 +247,8 @@ Some channels allow you to share links, but others don't. For instance, [general Each channel will have its rules posted in the "Info" section. -^ - -### 3. Setup your Profile, Avatar, and Cover Image +^ +### 3. Setup your Profile, Avatar, and Cover Image Under your user settings, you can update your profile. This includes your display name, location, about info, and website. @@ -278,17 +258,15 @@ To set your cover image, type or paste a link to the URL where the image is loca Once you have made all your changes, click the "Update" button to save your profile. -^ - -### 4. Choose your "NSFW" (Not Safe for Work) Display Preference +^ +### 4. Choose your "NSFW" (Not Safe for Work) Display Preference By default, content that users have tagged as "NSFW" will be hidden, but a link will be shown to reveal the content. You can update your display preference so that NSFW content is always shown by default, or is completely hidden with no option to reveal. -^ - -### 5. Create your "introduceyourself" post +^ +### 5. Create your "introduceyourself" post While not required, the tradition for new users is to create an "introduceyourself" post, to let the community know who you are. @@ -298,11 +276,11 @@ It is not required, but a lot of users will take a picture of themselves holding It is not required either, but if you have other social media accounts (Twitter, Facebook, etc.) you can help the community verify that you are who you say you are, by sharing the link to your Steemit introduceyourself post with those accounts. If you are claiming to be someone famous, this is pretty much expected. -^ +^ *** -## Helpful Posts from Steemit Users +## Helpful Posts from Steemit Users - [Posting and Markdown Basics](https://steemit.com/steemit/@thecryptofiend/markdown-basics-for-beginners) - [Tons of Ways to Spend Your Hard Earned STEEM/SBD](https://steemit.com/steem/@timcliff/the-steem-economy-tons-of-ways-to-spend-your-hard-earned-steem-sbd) @@ -337,16 +315,14 @@ It is not required either, but if you have other social media accounts (Twitter, - [Blogging Tools](https://steemit.com/blogging/@munteanu/blogging-tools) - [How to Create Different Types of Blog Content](https://steemit.com/writing/@jessicanicklos/how-to-create-different-types-of-blog-content-know-here-total-guide-line) -^ - -## Users to Follow +^ +## Users to Follow - @steemitblog - Official Steemit Announcements - @ned - Ned Scott, CEO and Co-Founder of Steemit -^ - -## Other Resources +^ +## Other Resources - [FAQ](https://steemit.com/faq.html) - [Steemit Help](https://www.steemithelp.net/) @@ -355,17 +331,15 @@ It is not required either, but if you have other social media accounts (Twitter, - [Steem Block Explorer](https://steemd.com/) - [Steem Blockchain Explorer](https://steemdb.com/) -^ - -## Live Help +^ +## Live Help Ask your general questions in the [help](https://steemit.chat/channel/help) channel of [steemit.chat](https://steemit.chat/home). Users in the channel will typically respond to questions within a few hours. New Member Support Community is a group of people dedicated to helping new users find their way around Steemit. You can find them in the [New Member Support Community](https://discord.gg/HYj4yvw) channel of Discord Chat. -^ - -## Third Party References +^ +## Third Party References Peerhub, BlockTrades, Bittrex, Steemit Chat, Steemit Help, New Member Support Community, and Discord Chat, as well as the tools listed under "Other Resources" are third party applications/services, and are not owned or maintained by Steemit, Inc. Their listing here does not constitute and endorsement or recommendation on behalf of Steemit, Inc. @@ -373,4 +347,4 @@ All of the links in the "Helpful Posts from Steemit Users" section were created Please use the third party tools and content at your own risk. -^ +^ -- GitLab From 12643bbf4355241dbc78fe7eb59413a7b597a7f4 Mon Sep 17 00:00:00 2001 From: plink01001 <31787860+plink01001@users.noreply.github.com> Date: Thu, 30 Nov 2017 16:47:20 -0800 Subject: [PATCH 095/399] Wording update --- doc/LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/LICENSE.md b/doc/LICENSE.md index 1d5e582c7..84c2b6e60 100644 --- a/doc/LICENSE.md +++ b/doc/LICENSE.md @@ -6,4 +6,4 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The Steemit logo file that is stored in the repository is **not** included in the above license. The Steemit brand and logo are protected by intellectual property laws, including copyright and other proprietary rights of the United States and other countries. The purpose is to allow Steemit to protect the brand and logo in ways that extend user safety. One may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the Steemit logo or brand, except as permitted by the doctrine of fair use, or as authorized in writing by Steemit, Inc. +The Steemit logo file that is stored in the repository is **not** included in the above license. The Steemit brand and logo are protected by intellectual property laws, including copyright and other proprietary rights of the United States and other countries. The purpose is to allow Steemit, Inc. to protect the brand and logo in ways that extend user safety. One may not make unauthorized commercial use of, reproduce, prepare derivative works, distribute copies, perform, or publicly display the Steemit logo or brand, except as permitted by the doctrine of fair use, or as authorized in writing by Steemit, Inc. -- GitLab From 1daf0c2e8af3d1416f832711ec089147326fe15a Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Fri, 1 Dec 2017 12:35:10 -0600 Subject: [PATCH 096/399] Hash scrolling in-line docs --- src/shared/UniversalRender.jsx | 53 ++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index 92b8494a4..bc9e9bb62 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -42,14 +42,43 @@ const calcOffsetRoot = (startEl) => { }; //BEGIN: SCROLL CODE +/** + * The maximum number of times to attempt scrolling to the target element/y position + * (total seconds of attempted scrolling is given by (SCROLL_TOP_TRIES * SCROLL_TOP_DELAY_MS)/1000 ) + * @type {number} + */ const SCROLL_TOP_TRIES = 50; +/** + * The number of milliseconds to delay between scroll attempts + * (total seconds of attempted scrolling is given by (SCROLL_TOP_TRIES * SCROLL_TOP_DELAY_MS)/1000 ) + * @type {number} + */ const SCROLL_TOP_DELAY_MS = 100; +/** + * The size of the vertical gap between the bottom of the fixed header and the top of the scrolled-to element. + * @type {number} + */ const SCROLL_TOP_EXTRA_PIXEL_OFFSET = 3; +/** + * number of pixels the document can move in the 'wrong' direction (opposite of intended scroll) this covers accidental scroll movements by users. + * @type {number} + */ const SCROLL_FUDGE_PIXELS = 10; +/** + * if document is being scrolled up this is set for prevDocumentInfo && documentInfo + * @type {string} + */ const SCROLL_DIRECTION_UP = 'up'; +/** + * if document is being scrolled down this is set for prevDocumentInfo && documentInfo + * @type {string} + */ const SCROLL_DIRECTION_DOWN = 'down'; -//If an element with this id is present, the page does not want us to detect whether we're navigating forward or not +/** + * If an element with this id is present, the page does not want us to detect navigation history direction (clicking links/forward button or back button) + * @type {string} + */ const DISABLE_ROUTER_HISTORY_NAV_DIRECTION_EL_ID = 'disable_router_nav_history_direction_check'; let scrollTopTimeout = null; @@ -73,6 +102,9 @@ const scrollTop = (el, topOffset, prevDocumentInfo, triesRemaining) => { direction: prevDocumentInfo.direction, }; let doScroll = false; + //for both SCROLL_DIRECTION_DOWN, SCROLL_DIRECTION_UP + //We scroll if the document has 1. not been deliberately scrolled, AND 2. we have not passed our target scroll, + //NOR has the document changed in a meaningful way since we last looked at it if(SCROLL_DIRECTION_DOWN === prevDocumentInfo.direction) { doScroll = (!(prevDocumentInfo.scrollTop > (documentInfo.scrollTop + SCROLL_FUDGE_PIXELS)) && (documentInfo.scrollTop < documentInfo.scrollTarget @@ -94,11 +126,15 @@ const scrollTop = (el, topOffset, prevDocumentInfo, triesRemaining) => { }; /** - * raison d'être: on hash link navigation, calculate the appropriate y-scroll with a fixed position top menu + * Custom scrolling behavior needed because we have chunky page loads and a fixed header. */ class OffsetScrollBehavior extends ScrollBehavior { + /** + * Raison d'être: on hash link navigation, assemble the needed info and pass it to scrollTop() + * In cases where we're scrolling to a pixel offset, adjust the offset for the current header, and punt to default behavior. + */ scrollToTarget(element, target) { - clearTimeout(scrollTopTimeout); + clearTimeout(scrollTopTimeout); //it's likely this will be called multiple times in succession, so clear and existing scrolling. const header = document.getElementsByTagName('header')[0]; //this dimension ideally would be pulled from a scss file. const topOffset = (((header)? header.offsetHeight : 0) + SCROLL_TOP_EXTRA_PIXEL_OFFSET) * (-1); const newTarget = []; //x coordinate @@ -124,7 +160,7 @@ class OffsetScrollBehavior extends ScrollBehavior { scrollTarget: calcOffsetRoot(el) + topOffset, }; documentInfo.direction = documentInfo.scrollTop < documentInfo.scrollTarget ? SCROLL_DIRECTION_DOWN : SCROLL_DIRECTION_UP; - scrollTop(el, topOffset, documentInfo, SCROLL_TOP_TRIES); + scrollTop(el, topOffset, documentInfo, SCROLL_TOP_TRIES); //this function does the actual work of scrolling. } else { super.scrollToTarget(element, newTarget); } @@ -191,12 +227,17 @@ async function universalRender({location, initial_state, offchain, ErrorPage, ta const history = syncHistoryWithStore(browserHistory, store); + /** + * When to scroll - on hash link navigation determine if the page should scroll to that element (forward nav, or ignore nav direction) + */ const scroll = useScroll({ - createScrollBehavior: config => new OffsetScrollBehavior(config), + createScrollBehavior: config => new OffsetScrollBehavior(config), //information assembler for has scrolling. shouldUpdateScroll: (prevLocation, {location}) => { // eslint-disable-line no-shadow - //we want to navigate to the corresponding id= element on 'PUSH' navigation (prev null + POP is a new window url nav ~= 'PUSH') + //if there is a hash, we may want to scroll to it if(location.hash) { + //if disableNavDirectionCheck exists, we want to always navigate to the hash (the page is telling us that's desired behavior based on the element's existence const disableNavDirectionCheck = document.getElementById(DISABLE_ROUTER_HISTORY_NAV_DIRECTION_EL_ID); + //we want to navigate to the corresponding id= element on 'PUSH' navigation (prev null + POP is a new window url nav ~= 'PUSH') if(disableNavDirectionCheck || (prevLocation === null && location.action === 'POP') || (location.action === 'PUSH') ) { -- GitLab From b9fdd79857e5fb2fd985ee5054f9fa0525bf7a04 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Fri, 1 Dec 2017 12:46:39 -0600 Subject: [PATCH 097/399] code changes for readability --- src/shared/UniversalRender.jsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index bc9e9bb62..9179b334f 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -105,13 +105,13 @@ const scrollTop = (el, topOffset, prevDocumentInfo, triesRemaining) => { //for both SCROLL_DIRECTION_DOWN, SCROLL_DIRECTION_UP //We scroll if the document has 1. not been deliberately scrolled, AND 2. we have not passed our target scroll, //NOR has the document changed in a meaningful way since we last looked at it - if(SCROLL_DIRECTION_DOWN === prevDocumentInfo.direction) { - doScroll = (!(prevDocumentInfo.scrollTop > (documentInfo.scrollTop + SCROLL_FUDGE_PIXELS)) + if(prevDocumentInfo.direction === SCROLL_DIRECTION_DOWN) { + doScroll = ((prevDocumentInfo.scrollTop <= (documentInfo.scrollTop + SCROLL_FUDGE_PIXELS)) && (documentInfo.scrollTop < documentInfo.scrollTarget || prevDocumentInfo.scrollTarget < documentInfo.scrollTarget || prevDocumentInfo.scrollHeight < documentInfo.scrollHeight)); - } else if(SCROLL_DIRECTION_UP === prevDocumentInfo.direction) { - doScroll = (!(prevDocumentInfo.scrollTop < (documentInfo.scrollTop - SCROLL_FUDGE_PIXELS)) + } else if(prevDocumentInfo.direction === SCROLL_DIRECTION_UP) { + doScroll = ((prevDocumentInfo.scrollTop >= (documentInfo.scrollTop - SCROLL_FUDGE_PIXELS)) && (documentInfo.scrollTop > documentInfo.scrollTarget || prevDocumentInfo.scrollTarget > documentInfo.scrollTarget || prevDocumentInfo.scrollHeight > documentInfo.scrollHeight)); @@ -136,7 +136,10 @@ class OffsetScrollBehavior extends ScrollBehavior { scrollToTarget(element, target) { clearTimeout(scrollTopTimeout); //it's likely this will be called multiple times in succession, so clear and existing scrolling. const header = document.getElementsByTagName('header')[0]; //this dimension ideally would be pulled from a scss file. - const topOffset = (((header)? header.offsetHeight : 0) + SCROLL_TOP_EXTRA_PIXEL_OFFSET) * (-1); + let topOffset = SCROLL_TOP_EXTRA_PIXEL_OFFSET * (-1); + if(header) { + topOffset += header.offsetHeight; + } const newTarget = []; //x coordinate let el = false; if(typeof target === 'string' ) { -- GitLab From 40d10dae483337878d8f6e105d2a191114c40e38 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Fri, 1 Dec 2017 14:35:40 -0600 Subject: [PATCH 098/399] HtmlReady.js tests for '#' leave-alone --- src/shared/HtmlReady.test.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/shared/HtmlReady.test.js b/src/shared/HtmlReady.test.js index 81faadeb9..a03e80edc 100644 --- a/src/shared/HtmlReady.test.js +++ b/src/shared/HtmlReady.test.js @@ -20,6 +20,16 @@ describe('htmlready', () => { expect(res).to.equal(dirty); }); + it('should allow in-page links ', () => { + const dirty = 'a link location'; + const res = HtmlReady(dirty).html; + expect(res).to.equal(dirty); + + const externalDomainDirty = 'Another website\'s apple section'; + const externalDomainResult = HtmlReady(externalDomainDirty).html; + expect(externalDomainResult).to.equal(externalDomainDirty); + }); + it('should not allow links where the text portion contains steemit.com but the link does not', () => { // There isn't an easy way to mock counterpart, even with proxyquire, so we just test for the missing translation message -- ugly but ok @@ -37,6 +47,19 @@ describe('htmlready', () => { const cleansednoendingslash = '
    https://steemit.com / https://steamit.com
    '; const resnoendingslash = HtmlReady(noendingslash).html; expect(resnoendingslash).to.equal(cleansednoendingslash); + + //make sure extra-domain in-page links are also caught by our phishy link scan. + const domainInpage = 'https://steemit.com'; + const cleanDomainInpage = '
    https://steemit.com / https://steamit.com#really-evil-inpage-component
    '; + const resDomainInpage = HtmlReady(domainInpage).html; + expect(resDomainInpage).to.equal(cleanDomainInpage); + + //misleading in-page links should also be caught + const inpage = 'Go down lower for https://steemit.com info!'; + const cleanInpage = '
    Go down lower for https://steemit.com info! / #https://steamit.com/unlikelyinpagelink
    '; + const resinpage = HtmlReady(inpage).html; + expect(resinpage).to.equal(cleanInpage); + }); it('should allow more than one link per post', () => { -- GitLab From fbecfae39f7b5552cc2913be7829c404a96602d9 Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Fri, 1 Dec 2017 20:47:56 -0600 Subject: [PATCH 099/399] restore topOffset value after code review prettification --- src/shared/UniversalRender.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index 9179b334f..82e4bf57a 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -138,7 +138,7 @@ class OffsetScrollBehavior extends ScrollBehavior { const header = document.getElementsByTagName('header')[0]; //this dimension ideally would be pulled from a scss file. let topOffset = SCROLL_TOP_EXTRA_PIXEL_OFFSET * (-1); if(header) { - topOffset += header.offsetHeight; + topOffset += header.offsetHeight * (-1); } const newTarget = []; //x coordinate let el = false; -- GitLab From 62e12553d525d9972e79a262ffc8ea9a81e947da Mon Sep 17 00:00:00 2001 From: Donovan Walker Date: Sat, 2 Dec 2017 00:22:55 -0600 Subject: [PATCH 100/399] Improve dropdown contrast & style consistency #2082 --- src/app/assets/stylesheets/_themes.scss | 123 +++++++++--------- src/app/assets/stylesheets/_variables.scss | 4 +- .../components/elements/AuthorDropdown.scss | 15 ++- 3 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/app/assets/stylesheets/_themes.scss b/src/app/assets/stylesheets/_themes.scss index f0da826f6..814b0e918 100755 --- a/src/app/assets/stylesheets/_themes.scss +++ b/src/app/assets/stylesheets/_themes.scss @@ -5,6 +5,7 @@ $themes: ( colorAccentReverse: $color-blue-original-light, colorWhite: $color-white, backgroundColor: $color-background-off-white, + backgroundColorEmphasis: $color-background-almost-white, backgroundColorOpaque: $color-background-off-white, backgroundTransparent: transparent, moduleBackgroundColor: $color-white, @@ -30,7 +31,7 @@ $themes: ( buttonText: $color-text-white, buttonTextShadow: 0 1px 0 rgba(0,0,0,0.20), buttonTextHover: $color-text-white, - buttonBoxShadow: $color-transparent, + buttonBoxShadow: $color-transparent, ), light: ( colorAccent: $color-teal, @@ -38,7 +39,8 @@ $themes: ( colorAccentReverse: $color-blue-black, colorWhite: $color-white, backgroundColor: $color-background-off-white, - backgroundColorOpaque: $color-background-off-white, + backgroundColorEmphasis: $color-background-almost-white, + backgroundColorOpaque: $color-background-off-white, backgroundTransparent: transparent, moduleBackgroundColor: $color-white, menuBackgroundColor: $color-background-dark, @@ -54,8 +56,8 @@ $themes: ( iconColorSecondary: #cacaca, textColorPrimary: $color-text-dark, textColorSecondary: $color-text-gray, - textColorAccent: $color-text-teal, - textColorAccentHover: $color-teal, + textColorAccent: $color-text-teal, + textColorAccentHover: $color-teal, textColorError: $color-text-red, contentBorderAccent: $color-teal, buttonBackground: $color-blue-black, @@ -64,7 +66,7 @@ $themes: ( buttonTextShadow: 0 1px 0 rgba(0,0,0,0.20), buttonTextHover: $color-white, buttonBoxShadow: $color-teal, - buttonBoxShadowHover: $color-blue-black, + buttonBoxShadowHover: $color-blue-black, ), dark: ( colorAccent: $color-teal, @@ -72,6 +74,7 @@ $themes: ( colorAccentReverse: $color-white, colorWhite: $color-white, backgroundColor: $color-background-dark, + backgroundColorEmphasis: $color-background-super-dark, backgroundColorOpaque: $color-blue-dark, moduleBackgroundColor: $color-background-dark, backgroundTransparent: transparent, @@ -125,74 +128,74 @@ $themes: ( .theme-original { - background-color: $white; + background-color: $white; color: $color-text-dark; @include MQ(M) { - background-color: $color-background-off-white; + background-color: $color-background-off-white; } } .theme-light { - background-color: $white; + background-color: $white; color: $color-text-dark; @include MQ(M) { - background-color: $color-background-off-white; - } + background-color: $color-background-off-white; + } } .theme-dark { - background-color: $color-background-dark; + background-color: $color-background-dark; color: $color-text-white; } -// Utility classes to be used with @extend +// Utility classes to be used with @extend .link { text-decoration: none; transition: 0.2s all ease-in-out; &--primary { @include themify($themes) { - color: themed('textColorPrimary'); + color: themed('textColorPrimary'); } &:visited, &:active { @include themify($themes) { - color: themed('textColorPrimary'); - } - } + color: themed('textColorPrimary'); + } + } &:hover, &:focus { @include themify($themes) { - color: themed('textColorAccent'); - } + color: themed('textColorAccent'); + } } } &--secondary { @include themify($themes) { - color: themed('textColorSecondary'); + color: themed('textColorSecondary'); } &:visited, &:active { @include themify($themes) { - color: themed('textColorSecondary'); - } - } + color: themed('textColorSecondary'); + } + } &:hover, &:focus { @include themify($themes) { - color: themed('textColorAccent'); - } - } + color: themed('textColorAccent'); + } + } } &--accent { @include themify($themes) { - color: themed('textColorAccent'); + color: themed('textColorAccent'); } &:visited, &:active { @include themify($themes) { - color: themed('textColorAccent'); - } - } + color: themed('textColorAccent'); + } + } &:hover, &:focus { @include themify($themes) { - color: themed('textColorAccentHover'); - } - } + color: themed('textColorAccentHover'); + } + } } } @@ -201,15 +204,15 @@ $themes: ( transition: 0.2s all ease-in-out; box-shadow: 0px 0px 0px 0 rgba(0,0,0,0); @include themify($themes) { - border: themed('borderAccent'); - color: themed('textColorAccent'); - } + border: themed('borderAccent'); + color: themed('textColorAccent'); + } &:hover { @include themify($themes) { - border: themed('borderDark'); - color: themed('textColorPrimary'); - } - } + border: themed('borderDark'); + color: themed('textColorPrimary'); + } + } } .e-btn { @@ -220,30 +223,30 @@ $themes: ( border-radius: 0; text-decoration: none; text-transform: capitalize; - @include font-size(18px); + @include font-size(18px); @include themify($themes) { - background-color: themed('buttonBackground'); - box-shadow: 0px 0px 0px 0 rgba(0,0,0,0), 5px 5px 0 0 themed('buttonBoxShadow'); + background-color: themed('buttonBackground'); + box-shadow: 0px 0px 0px 0 rgba(0,0,0,0), 5px 5px 0 0 themed('buttonBoxShadow'); color: themed('buttonText'); } &:hover, &:focus { @include themify($themes) { - background-color: themed('buttonBackgroundHover'); - box-shadow: 2px 2px 2px 0 rgba(0,0,0,0.1), 7px 7px 0 0 themed('buttonBoxShadowHover'); + background-color: themed('buttonBackgroundHover'); + box-shadow: 2px 2px 2px 0 rgba(0,0,0,0.1), 7px 7px 0 0 themed('buttonBoxShadowHover'); color: themed('buttonTextHover'); text-shadow: themed('buttonTextShadow'); } } &:visited, &:active { @include themify($themes) { - color: themed('buttonText'); + color: themed('buttonText'); } &:hover, &:focus { @include themify($themes) { - color: themed('buttonTextHover'); + color: themed('buttonTextHover'); } - } - } + } + } } .button.disabled, .button[disabled] { @@ -251,37 +254,37 @@ $themes: ( cursor: not-allowed; box-shadow: 0px 0px 0px 0 rgba(0,0,0,0); @include themify($themes) { - background-color: themed('buttonBackground'); + background-color: themed('buttonBackground'); box-shadow: 0px 0px 0px 0 rgba(0,0,0,0); color: themed('buttonText'); - } + } &:hover { @include themify($themes) { - background-color: themed('buttonBackground'); + background-color: themed('buttonBackground'); box-shadow: 0px 0px 0px 0 rgba(0,0,0,0); color: themed('buttonText'); - } + } } } // This button class doesn't applying theming (just straight styles). To be used when there are no theming classes available (e.g. in modals and static server pages in signup) -.e-btn { +.e-btn { &--black { background-color: $color-blue-black; - box-shadow: 0px 0px 0px 0 rgba(0,0,0,0), 5px 5px 0 0 $color-teal; + box-shadow: 0px 0px 0px 0 rgba(0,0,0,0), 5px 5px 0 0 $color-teal; color: $color-white; &:hover, &:focus { background-color: $color-teal; - box-shadow: 2px 2px 2px 0 rgba(0,0,0,0.1), 7px 7px 0 0 $color-blue-black; + box-shadow: 2px 2px 2px 0 rgba(0,0,0,0.1), 7px 7px 0 0 $color-blue-black; color: $color-white; text-shadow: 0 1px 0 rgba(0,0,0,0.20); } &:visited, &:active { background-color: $color-blue-black; - box-shadow: 0px 0px 0px 0 rgba(0,0,0,0), 5px 5px 0 0 $color-teal; + box-shadow: 0px 0px 0px 0 rgba(0,0,0,0), 5px 5px 0 0 $color-teal; color: $color-white; - } + } &.disabled, &[disabled] { opacity: 0.25; cursor: not-allowed; @@ -289,7 +292,7 @@ $themes: ( &:hover, &:focus { box-shadow: 0px 0px 0px 0 rgba(0,0,0,0); background-color: $color-blue-black; - color: $color-white; + color: $color-white; } } &.hollow { @@ -303,7 +306,7 @@ $themes: ( background-color: transparent; box-shadow: 0px 0px 0px 0 rgba(0,0,0,0); color: $color-blue-dark; - font-weight: normal; + font-weight: normal; text-shadow: 0 1px 0 rgba(0,0,0,0.0); } &:visited, &:active { @@ -311,7 +314,7 @@ $themes: ( box-shadow: 0px 0px 0px 0 rgba(0,0,0,0); color: $color-text-gray; font-weight: normal; - } + } } } } diff --git a/src/app/assets/stylesheets/_variables.scss b/src/app/assets/stylesheets/_variables.scss index 852bceb84..60ddc2c52 100755 --- a/src/app/assets/stylesheets/_variables.scss +++ b/src/app/assets/stylesheets/_variables.scss @@ -16,8 +16,10 @@ $color-teal-dark:#049173; $color-yellow: #fce76c; $color-transparent: transparent; +$color-background-almost-white:#fefefe; $color-background-off-white: #fcfcfc; $color-background-dark: #1C252B; +$color-background-super-dark: #10151b; $color-text-dark: #333; $color-text-white: #fcfcfc; @@ -34,4 +36,4 @@ $color-border-dark-lightest: #2B3A45; $font-primary: helvetica, sans-serif; -$alert-color: $color-red; \ No newline at end of file +$alert-color: $color-red; diff --git a/src/app/components/elements/AuthorDropdown.scss b/src/app/components/elements/AuthorDropdown.scss index 59b3f3044..4ab2b094b 100644 --- a/src/app/components/elements/AuthorDropdown.scss +++ b/src/app/components/elements/AuthorDropdown.scss @@ -8,14 +8,17 @@ width: 300px; border: 1px solid #cacaca; border-radius: 3px; - background-color: #fefefe; font-size: 1rem; - color: #333333; + @include themify($themes) { + background-color: themed('backgroundColorEmphasis'); + color: themed('textColorPrimary'); + } .Author__dropdown { width: 290px; min-height: 108px; + .Userpic { margin-right: 1rem; float: left; @@ -27,7 +30,9 @@ text-decoration: none; display: block; font-size: 110%; - color: #444; + @include themify($themes) { + color: themed('textColorPrimary'); + } font-weight: 600; line-height: 1; } @@ -36,7 +41,9 @@ text-decoration: none; font-size: 90%; font-weight: 400; - color: #666; + @include themify($themes) { + color: themed('textColorSecondary'); + } } .Author__bio { -- GitLab From 8b5ef58c9afd51d48b79bb7330b781b7a7d6c63b Mon Sep 17 00:00:00 2001 From: TimCliff Date: Sat, 2 Dec 2017 01:08:16 -0600 Subject: [PATCH 101/399] Update yarn to 1.3.2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fbdfc1cf..d33978b5b 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ mkdir tmp 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 4.1.2, and yarn 1.1.0. +Condenser is known to successfully build using node 8.7, npm 4.1.2, and yarn 1.3.2. Using nvm, you would install like this: ```bash -- GitLab From e08f43d2db9fe7d3cb20235b13ce6b0978d6e7b3 Mon Sep 17 00:00:00 2001 From: TimCliff Date: Sat, 2 Dec 2017 01:14:19 -0600 Subject: [PATCH 102/399] Update npm to 5.4.2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d33978b5b..14e243ce3 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ mkdir tmp 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 4.1.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: ```bash -- GitLab From 86c8f055da7b9a26031faabd14d85ed10ca4198c Mon Sep 17 00:00:00 2001 From: TimCliff Date: Sat, 2 Dec 2017 02:38:54 -0500 Subject: [PATCH 103/399] Closes #2084 remove anchor class --- src/app/help/en/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/help/en/faq.md b/src/app/help/en/faq.md index 332a3221e..5ada44bf4 100644 --- a/src/app/help/en/faq.md +++ b/src/app/help/en/faq.md @@ -1268,7 +1268,7 @@ You can also create a post on Steemit.com with the tag #help, and someone in the ^ -# Disclaimer +# Disclaimer ## Third Party References and User Links -- GitLab From dc61e7e4c4c6637c70bdbe263aca0a23db08521c Mon Sep 17 00:00:00 2001 From: TimCliff Date: Sat, 2 Dec 2017 02:40:25 -0500 Subject: [PATCH 104/399] remove unused anchor class --- src/app/assets/stylesheets/app.scss | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/app/assets/stylesheets/app.scss b/src/app/assets/stylesheets/app.scss index 388253c02..3ee911cc2 100644 --- a/src/app/assets/stylesheets/app.scss +++ b/src/app/assets/stylesheets/app.scss @@ -169,14 +169,6 @@ a.ptc { } } -.anchor { - padding-top: 68px; - margin-top: -68px; - position: relative; - display: block; - cursor: default; -} - h1, h2, h3, h4, h5, h6 { line-height: 1.2 !important; } -- GitLab From b7162edcd514134c4c7be6fc9d4ca30995bd359f Mon Sep 17 00:00:00 2001 From: oPen syLar Date: Sun, 3 Dec 2017 13:55:26 -0400 Subject: [PATCH 105/399] Fix es.json Fix strings over es.json --- src/app/locales/es.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/locales/es.json b/src/app/locales/es.json index a950c76db..1b04a06a4 100644 --- a/src/app/locales/es.json +++ b/src/app/locales/es.json @@ -206,9 +206,9 @@ "other": "%(count)s Respuestas" }, "post_key_warning": { - "confirm": "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", - "warning": "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", - "checkbox": "I understand" + "confirm": "Está a punto de publicar una clave privada STEEM o una contraseña maestra. Probablemente perderá el control de la cuenta asociada y todos sus fondos.", + "warning": "Los usuarios legítimos, incluidos los empleados de Steemit Inc., nunca le pedirán una clave privada o contraseña maestra.", + "checkbox": "Entiendo" } }, "navigation": { @@ -609,7 +609,7 @@ "from": "De", "to": "Para", "asset_currently_collecting": "%(asset)s recolectando actualmente %(interest)s%% APR.", - "beware_of_spam_and_phishing_links": "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." + "beware_of_spam_and_phishing_links": "Tenga cuidado con los enlaces de spam y phishing en los memos de transferencia. No abra enlaces de usuarios en los que no confíe. No proporcione sus claves privadas a sitios web de terceros." }, "userwallet_jsx": { "conversion_complete_tip": "Se completará el", -- GitLab From 528055b53bcb6e0acab3e4352e3a2f8676c5d061 Mon Sep 17 00:00:00 2001 From: oPen syLar Date: Sun, 3 Dec 2017 14:19:23 -0400 Subject: [PATCH 106/399] Change es.json Fix issue wrong strings translate --- src/app/locales/es.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/locales/es.json b/src/app/locales/es.json index 1b04a06a4..1fefd885d 100644 --- a/src/app/locales/es.json +++ b/src/app/locales/es.json @@ -553,10 +553,10 @@ "keep_me_logged_in": "Manténme logeado", "amazing_community": "comunidad increíble", "to_comment_and_reward_others": "para comentar y recompensar a otros", - "sign_up_get_steem": "Sign up. Get STEEM", - "signup_button": "Sign up now to earn ", + "sign_up_get_steem": "Crea una cuenta. Comienza en STEEM", + "signup_button": "Crea una cuenta, comienza a ganar ", "signup_button_emphasis": "FREE STEEM!", - "returning_users": "Usuarios que vuelven", + "returning_users": "Inicia sesion", "join_our": "Únete a nuestro" }, "chainvalidation_js": { -- GitLab From 467c98a32b8f6128c2d4142819b95a87d1c81dd1 Mon Sep 17 00:00:00 2001 From: oPen syLar Date: Sun, 3 Dec 2017 14:23:38 -0400 Subject: [PATCH 107/399] New change es.json Added spanish translate --- src/app/locales/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/locales/es.json b/src/app/locales/es.json index 1fefd885d..1de58434f 100644 --- a/src/app/locales/es.json +++ b/src/app/locales/es.json @@ -54,7 +54,7 @@ "password": "Contraseña", "payouts": "Pagos", "permissions": "Permisos", - "phishy_message": "Link expanded to plain text; beware of a potential phishing attempt", + "phishy_message": "Enlace expuesto a texto sin formato; cuidado con un posible intento de phishing", "post": "Publicación", "post_as": "Publicar como", "posts": "Publicaciones", -- GitLab From 36c2cd3cd620c17692d106de8970ef164128d0bf Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Sun, 3 Dec 2017 18:22:24 -0500 Subject: [PATCH 108/399] add prettier & yarn command `yarn run fmt` also, remove the existing precommit hooks until we fix them fixes #2090 --- .prettierrc | 5 +++++ README.md | 2 ++ package.json | 10 ++++------ yarn.lock | 4 ++++ 4 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..c38096929 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + tabWidth: 4, + singleQuote: true, + trailingComma: "es5" +} diff --git a/README.md b/README.md index aca00eedc..c117bf7db 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,8 @@ user@example:~$ tarantool We are using _[Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)_ with some modifications (see .eslintrc). Please run _eslint_ in the working directory before committing your changes and make sure you didn't introduce any new styling issues. +We use prettier to autofromat the code. 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 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. We adhere to BEM methodology with exception for Foundation classes, here is an example for the Header component: diff --git a/package.json b/package.json index c34506ac6..10744ff1f 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "test:watch:all": "npm test -- --watch --watch-extensions jsx", "test:watch": "npm run mocha -- --watch --watch-extensions jsx", "eslint": "LIST=`git diff-index --name-only HEAD | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi", + "fmt": "prettier --config .prettierrc --write src", "production": "NODE_ENV=production node lib/server/index.js", "start": "NODE_ENV=development ./node_modules/babel-cli/bin/babel-node.js ./webpack/dev-server.js", "webpush": "./node_modules/babel-cli/bin/babel-node.js ./scripts/webpush_notify.js", @@ -23,6 +24,7 @@ "license": "MIT", "dependencies": { "@steem/crypto-session": "git+https://github.com/steemit/crypto-session.git#83a90b319ce5bc6a70362d52a15a815de7e729bb", + "@steemit/steem-js": "0.6.7", "assert": "^1.3.0", "autoprefixer-loader": "^3.2.0", "babel-cli": "^6.22.2", @@ -128,7 +130,6 @@ "sequelize-cli": "^2.3.1", "speakingurl": "^9.0.0", "sqlite3": "^3.1.8", - "@steemit/steem-js": "0.6.7", "store": "^1.3.20", "style-loader": "^0.18.2", "svg-inline-loader": "^0.8.0", @@ -170,6 +171,7 @@ "mocha": "^2.4.5", "node-watch": "^0.5.5", "pre-commit": "^1.2.2", + "prettier": "1.8.2", "react-addons-perf": "15.4.2", "react-addons-test-utils": "15.4.2", "react-transform-catch-errors": "^1.0.1", @@ -185,9 +187,5 @@ "engines": { "node": ">=8.7.0", "npm": ">=5.4.2" - }, - "pre-commit": [ - "eslint", - "checktranslations" - ] + } } diff --git a/yarn.lock b/yarn.lock index 9df41c09b..8931da557 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6254,6 +6254,10 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier@1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.8.2.tgz#bff83e7fd573933c607875e5ba3abbdffb96aeb8" + pretty-hrtime@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" -- GitLab From 56f1c36521f24fdb680a052e499f8c1e05df6267 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Sun, 3 Dec 2017 21:57:06 -0500 Subject: [PATCH 109/399] json-ify config --- .prettierrc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.prettierrc b/.prettierrc index c38096929..a8d8fcb5b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ { - tabWidth: 4, - singleQuote: true, - trailingComma: "es5" + "tabWidth": 4, + "singleQuote": true, + "trailingComma": "es5" } -- GitLab From 0ccd246c456442a1b5fd36d2ea4d4f3d090acb30 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Mon, 27 Nov 2017 16:47:20 -0500 Subject: [PATCH 110/399] remove redux-modules switch to ducks style --- package.json | 1 - src/app/components/App.jsx | 6 +- src/app/components/cards/CardView.js | 4 +- src/app/components/cards/Comment.jsx | 8 +- src/app/components/cards/PostFull.jsx | 17 +- src/app/components/cards/PostSummary.jsx | 6 +- src/app/components/cards/PostsList.jsx | 10 +- .../components/elements/ChangePassword.jsx | 20 +- .../components/elements/ConvertToSteem.jsx | 17 +- src/app/components/elements/Follow.jsx | 8 +- src/app/components/elements/Keys.jsx | 7 +- .../elements/MarkNotificationRead.jsx | 3 +- src/app/components/elements/Reblog.jsx | 4 +- src/app/components/elements/ReplyEditor.jsx | 17 +- .../elements/SavingsWithdrawHistory.jsx | 28 +- src/app/components/elements/ShowKey.js | 8 +- .../components/elements/SuggestPassword.jsx | 4 +- .../components/elements/TransactionError.jsx | 8 +- src/app/components/elements/UserKeys.jsx | 12 +- src/app/components/elements/Voting.jsx | 8 +- .../modules/ArticleLayoutSelector.jsx | 6 +- .../modules/ConfirmTransactionForm.jsx | 6 +- src/app/components/modules/Dialogs.jsx | 4 +- src/app/components/modules/LoginForm.jsx | 24 +- src/app/components/modules/Modals.jsx | 24 +- src/app/components/modules/Powerdown.jsx | 16 +- src/app/components/modules/PromotePost.jsx | 7 +- src/app/components/modules/Settings.jsx | 11 +- src/app/components/modules/TopRightMenu.jsx | 15 +- src/app/components/modules/Transfer.jsx | 13 +- src/app/components/modules/UserWallet.jsx | 18 +- src/app/components/pages/CreateAccount.jsx | 13 +- src/app/components/pages/Market.jsx | 22 +- src/app/components/pages/PickAccount.jsx | 7 +- .../components/pages/PostPageNoCategory.jsx | 7 +- src/app/components/pages/PostsIndex.jsx | 13 +- .../components/pages/RecoverAccountStep2.jsx | 15 +- src/app/components/pages/UserProfile.jsx | 25 +- src/app/components/pages/Witnesses.jsx | 10 +- src/app/redux/AppReducer.js | 140 ++-- src/app/redux/AuthSaga.js | 9 +- src/app/redux/FetchDataSaga.js | 57 +- src/app/redux/FollowSaga.js | 36 +- src/app/redux/GlobalReducer.js | 678 +++++++++++------- src/app/redux/MarketReducer.js | 103 ++- src/app/redux/MarketSaga.js | 25 +- .../{Offchain.jsx => OffchainReducer.js} | 0 src/app/redux/PollDataSaga.js | 12 +- src/app/redux/RootReducer.js | 37 +- src/app/redux/SagaShared.js | 21 +- src/app/redux/Transaction.js | 140 ---- src/app/redux/TransactionReducer.js | 187 +++++ src/app/redux/TransactionSaga.js | 77 +- src/app/redux/User.js | 155 ---- src/app/redux/UserActions.js | 6 - src/app/redux/UserReducer.js | 345 +++++++++ src/app/redux/UserSaga.js | 89 +-- src/app/redux/tests/global.test.js | 6 +- src/shared/UniversalRender.jsx | 5 +- yarn.lock | 75 +- 60 files changed, 1521 insertions(+), 1134 deletions(-) rename src/app/redux/{Offchain.jsx => OffchainReducer.js} (100%) delete mode 100644 src/app/redux/Transaction.js create mode 100644 src/app/redux/TransactionReducer.js delete mode 100644 src/app/redux/User.js delete mode 100644 src/app/redux/UserActions.js create mode 100644 src/app/redux/UserReducer.js diff --git a/package.json b/package.json index c34506ac6..8b81be683 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,6 @@ "react-timeago": "^3.1.2", "redux": "^3.3.1", "redux-form": "5.3.4", - "redux-modules": "0.0.5", "redux-saga": "^0.9.5", "remarkable": "^1.7.1", "sanitize-html": "^1.11.4", diff --git a/src/app/components/App.jsx b/src/app/components/App.jsx index 60196e5f8..fd3a5d410 100644 --- a/src/app/components/App.jsx +++ b/src/app/components/App.jsx @@ -3,8 +3,7 @@ import {connect} from 'react-redux'; import AppPropTypes from 'app/utils/AppPropTypes'; import Header from 'app/components/modules/Header'; import LpFooter from 'app/components/modules/lp/LpFooter'; -import user from 'app/redux/User'; -import g from 'app/redux/GlobalReducer'; +import * as userActions from 'app/redux/UserReducer'; import TopRightMenu from 'app/components/modules/TopRightMenu'; import { browserHistory } from 'react-router'; import classNames from 'classnames'; @@ -336,12 +335,11 @@ export default connect( }, dispatch => ({ loginUser: () => - dispatch(user.actions.usernamePasswordLogin()), + dispatch(userActions.usernamePasswordLogin({})), depositSteem: (username) => { const new_window = window.open(); new_window.opener = null; new_window.location = 'https://blocktrades.us/?input_coin_type=btc&output_coin_type=steem&receive_address=' + username; - //dispatch(g.actions.showDialog({name: 'blocktrades_deposit', params: {outputCoinType: 'VESTS'}})); }, }) )(App); diff --git a/src/app/components/cards/CardView.js b/src/app/components/cards/CardView.js index d05d1873a..52ad7d475 100644 --- a/src/app/components/cards/CardView.js +++ b/src/app/components/cards/CardView.js @@ -1,7 +1,7 @@ import React from 'react'; import {connect} from 'react-redux' import Link from 'app/components/elements/Link' -import g from 'app/redux/GlobalReducer' +import * as globalActions from 'app/redux/GlobalReducer'; import links from 'app/utils/Links' import tt from 'counterpart'; @@ -67,7 +67,7 @@ export default connect( }, dispatch => ({ clearMetaElement: (formId, element) => { - dispatch(g.actions.clearMetaElement({formId, element})) + dispatch(globalActions.clearMetaElement({formId, element})) } }) )(CardView) diff --git a/src/app/components/cards/Comment.jsx b/src/app/components/cards/Comment.jsx index 873bc7bf2..8ee2e780c 100644 --- a/src/app/components/cards/Comment.jsx +++ b/src/app/components/cards/Comment.jsx @@ -6,10 +6,10 @@ import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' import Voting from 'app/components/elements/Voting'; import { connect } from 'react-redux'; import { Link } from 'react-router'; -import user from 'app/redux/User'; +import * as userActions from 'app/redux/UserReducer'; import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper'; import Userpic from 'app/components/elements/Userpic'; -import transaction from 'app/redux/Transaction' +import * as transactionActions from 'app/redux/TransactionReducer'; import tt from 'counterpart'; import {parsePayoutAmount} from 'app/utils/ParsersAndFormatters'; import {Long} from 'bytebuffer'; @@ -400,9 +400,9 @@ const Comment = connect( // mapDispatchToProps dispatch => ({ - unlock: () => { dispatch(user.actions.showLogin()) }, + unlock: () => { dispatch(userActions.showLogin()) }, deletePost: (author, permlink) => { - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'delete_comment', operation: {author, permlink}, confirm: tt('g.are_you_sure'), diff --git a/src/app/components/cards/PostFull.jsx b/src/app/components/cards/PostFull.jsx index 549490f5b..e3d7f4cd4 100644 --- a/src/app/components/cards/PostFull.jsx +++ b/src/app/components/cards/PostFull.jsx @@ -3,8 +3,9 @@ import { Link } from 'react-router'; import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper'; import Icon from 'app/components/elements/Icon'; import { connect } from 'react-redux'; -import user from 'app/redux/User'; -import transaction from 'app/redux/Transaction' +import * as userActions from 'app/redux/UserReducer'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; import Voting from 'app/components/elements/Voting'; import Reblog from 'app/components/elements/Reblog'; import MarkdownViewer from 'app/components/cards/MarkdownViewer'; @@ -375,21 +376,21 @@ export default connect( // mapDispatchToProps dispatch => ({ - dispatchSubmit: (data) => { dispatch(user.actions.usernamePasswordLogin({...data})) }, - clearError: () => { dispatch(user.actions.loginError({error: null})) }, - unlock: () => { dispatch(user.actions.showLogin()) }, + dispatchSubmit: (data) => { dispatch(userActions.usernamePasswordLogin({...data})) }, + clearError: () => { dispatch(userActions.loginError({error: null})) }, + unlock: () => { dispatch(userActions.showLogin()) }, deletePost: (author, permlink) => { - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'delete_comment', operation: {author, permlink}, confirm: tt('g.are_you_sure') })); }, showPromotePost: (author, permlink) => { - dispatch({type: 'global/SHOW_DIALOG', payload: {name: 'promotePost', params: {author, permlink}}}); + dispatch(globalActions.showDialog({ name: 'promotePost', params: { author, permlink } })); }, showExplorePost: (permlink) => { - dispatch({type: 'global/SHOW_DIALOG', payload: {name: 'explorePost', params: {permlink}}}); + dispatch(globalActions.showDialog({ name: 'explorePost', params: { permlink } })); }, }) )(PostFull) diff --git a/src/app/components/cards/PostSummary.jsx b/src/app/components/cards/PostSummary.jsx index 75c83f1f0..94b3cd4fb 100644 --- a/src/app/components/cards/PostSummary.jsx +++ b/src/app/components/cards/PostSummary.jsx @@ -3,7 +3,7 @@ import { Link } from 'react-router'; import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper'; import Icon from 'app/components/elements/Icon'; import { connect } from 'react-redux'; -import user from 'app/redux/User'; +import * as userActions from 'app/redux/UserReducer'; import Reblog from 'app/components/elements/Reblog'; import Voting from 'app/components/elements/Voting'; import {immutableAccessor} from 'app/utils/Accessors'; @@ -271,7 +271,7 @@ export default connect( }, (dispatch) => ({ - dispatchSubmit: data => { dispatch(user.actions.usernamePasswordLogin({...data})) }, - clearError: () => { dispatch(user.actions.loginError({error: null})) } + dispatchSubmit: data => { dispatch(userActions.usernamePasswordLogin({...data})) }, + clearError: () => { dispatch(userActions.loginError({error: null})) } }) )(PostSummary) diff --git a/src/app/components/cards/PostsList.jsx b/src/app/components/cards/PostsList.jsx index 751a5c193..12bf6c0ea 100644 --- a/src/app/components/cards/PostsList.jsx +++ b/src/app/components/cards/PostsList.jsx @@ -1,4 +1,8 @@ import React, {PropTypes} from 'react'; +import {connect} from 'react-redux'; +import tt from 'counterpart'; +import * as userActions from 'app/redux/UserReducer'; +import { actions as fetchDataSagaActions } from 'app/redux/FetchDataSaga'; import PostSummary from 'app/components/cards/PostSummary'; import Post from 'app/components/pages/Post'; import LoadingIndicator from 'app/components/elements/LoadingIndicator'; @@ -7,8 +11,6 @@ import CloseButton from 'react-foundation-components/lib/global/close-button'; import {findParent} from 'app/utils/DomUtils'; import Icon from 'app/components/elements/Icon'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; -import {connect} from 'react-redux' -import tt from 'counterpart'; function topPosition(domElt) { if (!domElt) { @@ -168,10 +170,10 @@ export default connect( }, dispatch => ({ fetchState: (pathname) => { - dispatch({type: 'FETCH_STATE', payload: {pathname}}) + dispatch(fetchDataSagaActions.fetchState({pathname})); }, removeHighSecurityKeys: () => { - dispatch({type: 'user/REMOVE_HIGH_SECURITY_KEYS'}) + dispatch(userActions.removeHighSecurityKeys()); } }) )(PostsList) diff --git a/src/app/components/elements/ChangePassword.jsx b/src/app/components/elements/ChangePassword.jsx index 97302926e..207a84ecf 100644 --- a/src/app/components/elements/ChangePassword.jsx +++ b/src/app/components/elements/ChangePassword.jsx @@ -1,14 +1,16 @@ /* eslint react/prop-types: 0 */ import React from 'react' -import transaction from 'app/redux/Transaction' -import LoadingIndicator from 'app/components/elements/LoadingIndicator' -import {validate_account_name} from 'app/utils/ChainValidation' -import {cleanReduxInput} from 'app/utils/ReduxForms' import tt from 'counterpart'; -import { APP_NAME } from 'app/client_config'; import {PrivateKey, PublicKey, key_utils} from '@steemit/steem-js/lib/auth/ecc'; import {api} from '@steemit/steem-js'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as appActions from 'app/redux/AppReducer'; +import LoadingIndicator from 'app/components/elements/LoadingIndicator'; +import {validate_account_name} from 'app/utils/ChainValidation'; +import {cleanReduxInput} from 'app/utils/ReduxForms'; +import { APP_NAME } from 'app/client_config'; + const {string, oneOf} = React.PropTypes class ChangePassword extends React.Component { @@ -252,7 +254,7 @@ export default reduxForm( {authType: 'posting', oldAuth: password, newAuth: ph('posting', newWif)}, {authType: 'memo', oldAuth: password, newAuth: ph('memo', newWif)}, ] - dispatch(transaction.actions.updateAuthorities({ + dispatch(transactionActions.updateAuthorities({ twofa, // signingKey provides the password if it was not provided in auths signingKey: authType ? password : null, @@ -262,11 +264,11 @@ export default reduxForm( })) }, notify: (message) => { - dispatch({type: 'ADD_NOTIFICATION', payload: { + dispatch(appActions.addNotification({ key: "chpwd_" + Date.now(), message, - dismissAfter: 5000} - }); + dismissAfter: 5000, + })); }, }) )(ChangePassword) diff --git a/src/app/components/elements/ConvertToSteem.jsx b/src/app/components/elements/ConvertToSteem.jsx index a1ffad4a3..63b65e8cc 100644 --- a/src/app/components/elements/ConvertToSteem.jsx +++ b/src/app/components/elements/ConvertToSteem.jsx @@ -2,7 +2,8 @@ import React from 'react' import ReactDOM from 'react-dom'; import {reduxForm} from 'redux-form'; // @deprecated, instead use: app/utils/ReactForm.js -import transaction from 'app/redux/Transaction' +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as appActions from 'app/redux/AppReducer'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' import TransactionError from 'app/components/elements/TransactionError' import LoadingIndicator from 'app/components/elements/LoadingIndicator' @@ -103,17 +104,17 @@ export default reduxForm( const amount = [parseFloat(amt).toFixed(3), DEBT_TICKER].join(" ") const requestid = Math.floor(Date.now() / 1000) const conf = tt('postfull_jsx.in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN', { amount: amount.split(' ')[0], DEBT_TOKEN, LIQUID_TOKEN}) - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'convert', operation: {owner, requestid, amount}, confirm: conf + '?', successCallback: () => { - success() - dispatch({type: 'ADD_NOTIFICATION', payload: - {key: "convert_sd_to_steem_" + Date.now(), - message: tt('g.order_placed') + ': ' + conf, - dismissAfter: 5000} - }) + success(); + dispatch(appActions.addNotification({ + key: "convert_sd_to_steem_" + Date.now(), + message: tt('g.order_placed') + ': ' + conf, + dismissAfter: 5000, + })); }, errorCallback: () => {error()} })) diff --git a/src/app/components/elements/Follow.jsx b/src/app/components/elements/Follow.jsx index 313d5c767..c250214e8 100644 --- a/src/app/components/elements/Follow.jsx +++ b/src/app/components/elements/Follow.jsx @@ -2,10 +2,10 @@ import React, {PropTypes} from 'react'; import {connect} from 'react-redux'; import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; -import transaction from 'app/redux/Transaction'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as userActions from 'app/redux/UserReducer'; import {Set, Map} from 'immutable' import tt from 'counterpart'; -import user from 'app/redux/User'; const {string, bool, any} = PropTypes; @@ -135,7 +135,7 @@ module.exports = connect( updateFollow: (follower, following, action, done) => { const what = action ? [action] : []; const json = ['follow', {follower, following, what}]; - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'custom_json', operation: { id: 'follow', @@ -148,7 +148,7 @@ module.exports = connect( }, showLogin: e => { if (e) e.preventDefault(); - dispatch(user.actions.showLogin()) + dispatch(userActions.showLogin()) }, }) )(Follow); diff --git a/src/app/components/elements/Keys.jsx b/src/app/components/elements/Keys.jsx index a82226a44..9c7d542f9 100644 --- a/src/app/components/elements/Keys.jsx +++ b/src/app/components/elements/Keys.jsx @@ -2,8 +2,7 @@ import React, {PropTypes, Component} from 'react' import {Map, List} from 'immutable' import {connect} from 'react-redux' -import user from 'app/redux/User' -import g from 'app/redux/GlobalReducer' +import * as globalActions from 'app/redux/GlobalReducer'; import ShowKey from 'app/components/elements/ShowKey' import tt from 'counterpart'; @@ -111,8 +110,8 @@ export default connect( dispatch => ({ showChangePassword: (username, authType, priorAuthKey) => { const name = 'changePassword' - dispatch(g.actions.remove({key: name})) - dispatch(g.actions.showDialog({name, params: {username, authType, priorAuthKey}})) + dispatch(globalActions.remove({key: name})) + dispatch(globalActions.showDialog({name, params: {username, authType, priorAuthKey}})) }, }) )(Keys) diff --git a/src/app/components/elements/MarkNotificationRead.jsx b/src/app/components/elements/MarkNotificationRead.jsx index 61f8c00a5..5998e6b87 100644 --- a/src/app/components/elements/MarkNotificationRead.jsx +++ b/src/app/components/elements/MarkNotificationRead.jsx @@ -1,6 +1,7 @@ import React from 'react'; import {connect} from 'react-redux'; import {markNotificationRead} from 'app/utils/ServerApiClient'; +import * as appActions from 'app/redux/AppReducer'; class MarkNotificationRead extends React.Component { @@ -27,5 +28,5 @@ class MarkNotificationRead extends React.Component { } export default connect(null, dispatch => ({ - update: (payload) => { dispatch({type: 'UPDATE_NOTIFICOUNTERS', payload})}, + update: (payload) => { dispatch(appActions.updateNotificounters(payload)) }, }))(MarkNotificationRead); diff --git a/src/app/components/elements/Reblog.jsx b/src/app/components/elements/Reblog.jsx index 33fe15407..c31ccd6a0 100644 --- a/src/app/components/elements/Reblog.jsx +++ b/src/app/components/elements/Reblog.jsx @@ -2,7 +2,7 @@ import React, {PropTypes} from 'react'; import {connect} from 'react-redux'; // import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' -import transaction from 'app/redux/Transaction'; +import * as transactionActions from 'app/redux/TransactionReducer'; import Icon from 'app/components/elements/Icon'; import tt from 'counterpart'; @@ -81,7 +81,7 @@ module.exports = connect( dispatch => ({ reblog: (account, author, permlink, successCallback, errorCallback) => { const json = ['reblog', {account, author, permlink}] - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'custom_json', confirm: tt('g.are_you_sure'), operation: { diff --git a/src/app/components/elements/ReplyEditor.jsx b/src/app/components/elements/ReplyEditor.jsx index 370bfbd66..cb4d370df 100644 --- a/src/app/components/elements/ReplyEditor.jsx +++ b/src/app/components/elements/ReplyEditor.jsx @@ -1,6 +1,6 @@ import React from 'react'; import reactForm from 'app/utils/ReactForm' -import transaction from 'app/redux/Transaction'; +import * as transactionActions from 'app/redux/TransactionReducer'; import MarkdownViewer from 'app/components/cards/MarkdownViewer' import CategorySelector from 'app/components/cards/CategorySelector' import {validateCategory} from 'app/components/cards/CategorySelector' @@ -10,7 +10,7 @@ import Tooltip from 'app/components/elements/Tooltip' import sanitizeConfig, {allowedTags} from 'app/utils/SanitizeConfig' import sanitize from 'sanitize-html' import HtmlReady from 'shared/HtmlReady' -import g from 'app/redux/GlobalReducer' +import * as globalActions from 'app/redux/GlobalReducer'; import {Set} from 'immutable' import Remarkable from 'remarkable' import Dropzone from 'react-dropzone' @@ -527,17 +527,12 @@ export default (formId) => connect( // mapDispatchToProps dispatch => ({ clearMetaData: (id) => { - dispatch(g.actions.clearMeta({id})) + dispatch(globalActions.clearMeta({id})) }, setMetaData: (id, jsonMetadata) => { - dispatch(g.actions.setMetaData({id, meta: jsonMetadata ? jsonMetadata.steem : null})) - }, - uploadImage: (file, progress) => { - dispatch({ - type: 'user/UPLOAD_IMAGE', - payload: {file, progress}, - }) + dispatch(globalActions.setMetaData({id, meta: jsonMetadata ? jsonMetadata.steem : null})) }, + uploadImage: (file, progress) => dispatch(userActions.uploadImage({ file, progress })), reply: ({category, title, body, author, permlink, parent_author, parent_permlink, isHtml, isStory, type, originalPost, autoVote = false, payoutType = '50%', state, jsonMetadata, @@ -647,7 +642,7 @@ export default (formId) => connect( json_metadata: meta, __config } - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'comment', operation, errorCallback, diff --git a/src/app/components/elements/SavingsWithdrawHistory.jsx b/src/app/components/elements/SavingsWithdrawHistory.jsx index 8e63db496..604c6647b 100644 --- a/src/app/components/elements/SavingsWithdrawHistory.jsx +++ b/src/app/components/elements/SavingsWithdrawHistory.jsx @@ -1,11 +1,14 @@ /* eslint react/prop-types: 0 */ -import React from 'react' -import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' -import LoadingIndicator from 'app/components/elements/LoadingIndicator' -import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper' -import transaction from 'app/redux/Transaction' -import Memo from 'app/components/elements/Memo' -import tt from 'counterpart' +import React from 'react'; +import tt from 'counterpart'; + +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; +import * as userActions from 'app/redux/UserReducer'; +import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; +import LoadingIndicator from 'app/components/elements/LoadingIndicator'; +import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper'; +import Memo from 'app/components/elements/Memo'; class SavingsWithdrawHistory extends React.Component { @@ -99,20 +102,15 @@ export default connect( } }, dispatch => ({ - loadHistory: () => { - dispatch({ - type: 'user/LOAD_SAVINGS_WITHDRAW', - payload: {}, - }) - }, + loadHistory: () => dispatch(userActions.loadSavingsWithdraw({})), cancelWithdraw: (fro, request_id, success, errorCallback) => { const confirm = tt('savingswithdrawhistory_jsx.cancel_this_withdraw_request') const successCallback = () => { // refresh transfer history - dispatch({type: 'global/GET_STATE', payload: {url: `@${fro}/transfers`}}) + dispatch(globalActions.getState({ url: `@${fro}/transfers` })); success() } - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'cancel_transfer_from_savings', operation: {from: fro, request_id}, confirm, diff --git a/src/app/components/elements/ShowKey.js b/src/app/components/elements/ShowKey.js index 3df99d0ba..f8db6b8b5 100644 --- a/src/app/components/elements/ShowKey.js +++ b/src/app/components/elements/ShowKey.js @@ -1,9 +1,9 @@ import React, {PropTypes, Component} from 'react'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; import {connect} from 'react-redux'; -import user from 'app/redux/User'; +import * as userActions from 'app/redux/UserReducer'; import tt from 'counterpart'; -import g from 'app/redux/GlobalReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; /** Display a public key. Offer to show a private key, but only if it matches the provided public key */ class ShowKey extends Component { @@ -100,10 +100,10 @@ export default connect( (state, ownProps) => ownProps, dispatch => ({ showLogin: ({username, authType}) => { - dispatch(user.actions.showLogin({loginDefault: {username, authType}})) + dispatch(userActions.showLogin({loginDefault: {username, authType}})) }, showQRKey: ({type, isPrivate, text}) => { - dispatch(g.actions.showDialog({name: "qr_key", params: {type, isPrivate, text}})); + dispatch(globalActions.showDialog({name: "qr_key", params: {type, isPrivate, text}})); } }) )(ShowKey) diff --git a/src/app/components/elements/SuggestPassword.jsx b/src/app/components/elements/SuggestPassword.jsx index ff6220763..6d996652b 100644 --- a/src/app/components/elements/SuggestPassword.jsx +++ b/src/app/components/elements/SuggestPassword.jsx @@ -2,7 +2,7 @@ import React from 'react' import {connect} from 'react-redux' import {renderToString} from 'react-dom/server' -import g from 'app/redux/GlobalReducer' +import * as globalActions from 'app/redux/GlobalReducer' import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' import Icon from 'app/components/elements/Icon' import tt from 'counterpart'; @@ -99,7 +99,7 @@ export default connect( const PASSWORD_LENGTH = 32 const private_key = key_utils.get_random_key() const suggestedPassword = private_key.toWif().substring(3, 3 + PASSWORD_LENGTH) - dispatch(g.actions.set({key: 'suggestedPassword', value: suggestedPassword})) + dispatch(globalActions.set({key: 'suggestedPassword', value: suggestedPassword})) }, }) )(SuggestPassword) diff --git a/src/app/components/elements/TransactionError.jsx b/src/app/components/elements/TransactionError.jsx index 78da84ac4..7fbfacfb3 100644 --- a/src/app/components/elements/TransactionError.jsx +++ b/src/app/components/elements/TransactionError.jsx @@ -1,6 +1,6 @@ import React from 'react' import {connect} from 'react-redux' -import transaction from 'app/redux/Transaction' +import * as transactionActions from 'app/redux/TransactionReducer'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' import {Map} from 'immutable' @@ -62,11 +62,11 @@ export default connect( // mapDispatchToProps dispatch => ({ addListener: (opType) => { - dispatch(transaction.actions.set({key: ['TransactionError', opType + '_listener'], value: true})) + dispatch(transactionActions.set({key: ['TransactionError', opType + '_listener'], value: true})) }, removeListener: (opType) => { - dispatch(transaction.actions.remove({key: ['TransactionError', opType]})) - dispatch(transaction.actions.remove({key: ['TransactionError', opType + '_listener']})) + dispatch(transactionActions.remove({key: ['TransactionError', opType]})) + dispatch(transactionActions.remove({key: ['TransactionError', opType + '_listener']})) }, }) )(TransactionError) diff --git a/src/app/components/elements/UserKeys.jsx b/src/app/components/elements/UserKeys.jsx index cacf8594f..324884f59 100644 --- a/src/app/components/elements/UserKeys.jsx +++ b/src/app/components/elements/UserKeys.jsx @@ -1,10 +1,10 @@ import React, {PropTypes, Component} from 'react' -import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' -import Keys from 'app/components/elements/Keys' -import g from 'app/redux/GlobalReducer' import {connect} from 'react-redux'; import QRCode from 'react-qr' import tt from 'counterpart'; +import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' +import Keys from 'app/components/elements/Keys' +import * as globalActions from 'app/redux/GlobalReducer'; const keyTypes = ['Posting', 'Active', 'Owner', 'Memo'] @@ -114,12 +114,12 @@ export default connect( }, dispatch => ({ setWifShown: (shown) => { - dispatch(g.actions.receiveState({UserKeys_wifShown: shown})) + dispatch(globalActions.receiveState({UserKeys_wifShown: shown})) }, showChangePassword: (username) => { const name = 'changePassword' - dispatch(g.actions.remove({key: name})) - dispatch(g.actions.showDialog({name, params: {username}})) + dispatch(globalActions.remove({key: name})) + dispatch(globalActions.showDialog({name, params: {username}})) }, }) )(UserKeys) diff --git a/src/app/components/elements/Voting.jsx b/src/app/components/elements/Voting.jsx index 23962a96c..ebae5caac 100644 --- a/src/app/components/elements/Voting.jsx +++ b/src/app/components/elements/Voting.jsx @@ -1,7 +1,9 @@ import React from 'react'; import { connect } from 'react-redux'; -import transaction from 'app/redux/Transaction'; import Slider from 'react-rangeslider'; +import tt from 'counterpart'; +import CloseButton from 'react-foundation-components/lib/global/close-button'; +import * as transactionActions from 'app/redux/TransactionReducer'; import Icon from 'app/components/elements/Icon'; import FormattedAsset from 'app/components/elements/FormattedAsset'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; @@ -9,8 +11,6 @@ import {formatDecimal, parsePayoutAmount} from 'app/utils/ParsersAndFormatters'; import DropdownMenu from 'app/components/elements/DropdownMenu'; import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper'; import FoundationDropdown from 'app/components/elements/FoundationDropdown'; -import CloseButton from 'react-foundation-components/lib/global/close-button'; -import tt from 'counterpart'; const ABOUT_FLAG =

    {tt('voting_jsx.flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following')}

    @@ -299,7 +299,7 @@ export default connect( if(weight < 0) return tt('voting_jsx.changing_to_a_downvote') + t return null } - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'vote', operation: {voter: username, author, permlink, weight, __config: {title: weight < 0 ? tt('voting_jsx.confirm_flag') : null}, diff --git a/src/app/components/modules/ArticleLayoutSelector.jsx b/src/app/components/modules/ArticleLayoutSelector.jsx index 952a5f8c3..23da03072 100644 --- a/src/app/components/modules/ArticleLayoutSelector.jsx +++ b/src/app/components/modules/ArticleLayoutSelector.jsx @@ -1,7 +1,7 @@ /* eslint react/prop-types: 0 */ import React, { PropTypes } from 'react'; import { connect } from 'react-redux'; -import user from 'app/redux/User'; +import * as appActions from 'app/redux/AppReducer'; class ArticleLayoutSelector extends React.Component { render() { @@ -26,7 +26,7 @@ export default connect( }), dispatch => ({ toggleBlogmode: () => { - dispatch({ type: 'TOGGLE_BLOGMODE' }); + dispatch(appActions.toggleBlogmode()); }, }), -)(ArticleLayoutSelector); \ No newline at end of file +)(ArticleLayoutSelector); diff --git a/src/app/components/modules/ConfirmTransactionForm.jsx b/src/app/components/modules/ConfirmTransactionForm.jsx index c1ce794a3..da6f44931 100644 --- a/src/app/components/modules/ConfirmTransactionForm.jsx +++ b/src/app/components/modules/ConfirmTransactionForm.jsx @@ -1,6 +1,6 @@ import React, { PropTypes, Component } from 'react'; import {connect} from 'react-redux' -import transaction from 'app/redux/Transaction' +import * as transactionActions from 'app/redux/TransactionReducer'; import {findParent} from 'app/utils/DomUtils'; import tt from 'counterpart'; @@ -95,8 +95,8 @@ export default connect( // mapDispatchToProps dispatch => ({ okClick: (confirmBroadcastOperation) => { - dispatch(transaction.actions.hideConfirm()) - dispatch(transaction.actions.broadcastOperation({...(confirmBroadcastOperation.toJS())})) + dispatch(transactionActions.hideConfirm()) + dispatch(transactionActions.broadcastOperation({...(confirmBroadcastOperation.toJS())})) } }) )(ConfirmTransactionForm) diff --git a/src/app/components/modules/Dialogs.jsx b/src/app/components/modules/Dialogs.jsx index ddfd4405b..f3a2e163b 100644 --- a/src/app/components/modules/Dialogs.jsx +++ b/src/app/components/modules/Dialogs.jsx @@ -2,8 +2,8 @@ import React from 'react'; import {connect} from 'react-redux'; import CloseButton from 'react-foundation-components/lib/global/close-button'; import Reveal from 'react-foundation-components/lib/global/reveal'; -import g from 'app/redux/GlobalReducer' import {Map, List} from 'immutable' +import * as globalActions from 'app/redux/GlobalReducer'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; import QrReader from 'app/components/elements/QrReader' import ConvertToSteem from 'app/components/elements/ConvertToSteem' @@ -99,7 +99,7 @@ export default connect( }, dispatch => ({ hide: name => { - dispatch(g.actions.hideDialog({name})) + dispatch(globalActions.hideDialog({name})) }, }) )(Dialogs) diff --git a/src/app/components/modules/LoginForm.jsx b/src/app/components/modules/LoginForm.jsx index 0dde2bf74..29e79dbb7 100644 --- a/src/app/components/modules/LoginForm.jsx +++ b/src/app/components/modules/LoginForm.jsx @@ -1,8 +1,8 @@ /* eslint react/prop-types: 0 */ import React, { PropTypes, Component } from 'react'; -import transaction from 'app/redux/Transaction' -import g from 'app/redux/GlobalReducer' -import user from 'app/redux/User' +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; +import * as userActions from 'app/redux/UserReducer'; import {validate_account_name} from 'app/utils/ChainValidation'; import runTests from 'app/utils/BrowserTests'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' @@ -305,21 +305,21 @@ export default connect( const username = data.username.trim().toLowerCase() if (loginBroadcastOperation) { const {type, operation, successCallback, errorCallback} = loginBroadcastOperation.toJS() - dispatch(transaction.actions.broadcastOperation({type, operation, username, password, successCallback, errorCallback})) - dispatch(user.actions.usernamePasswordLogin({username, password, saveLogin, afterLoginRedirectToWelcome, operationType: type})) - dispatch(user.actions.closeLogin()) + dispatch(transactionActions.broadcastOperation({type, operation, username, password, successCallback, errorCallback})) + dispatch(userActions.usernamePasswordLogin({username, password, saveLogin, afterLoginRedirectToWelcome, operationType: type})) + dispatch(userActions.closeLogin()) } else { - dispatch(user.actions.usernamePasswordLogin({username, password, saveLogin, afterLoginRedirectToWelcome})) + dispatch(userActions.usernamePasswordLogin({username, password, saveLogin, afterLoginRedirectToWelcome})) } }, - clearError: () => { if (hasError) dispatch(user.actions.loginError({error: null})) }, + clearError: () => { if (hasError) dispatch(userActions.loginError({error: null})) }, qrReader: (dataCallback) => { - dispatch(g.actions.showDialog({name: 'qr_reader', params: {handleScan: dataCallback}})); + dispatch(globalActions.showDialog({name: 'qr_reader', params: {handleScan: dataCallback}})); }, showChangePassword: (username, defaultPassword) => { - dispatch(user.actions.closeLogin()) - dispatch(g.actions.remove({key: 'changePassword'})) - dispatch(g.actions.showDialog({name: 'changePassword', params: {username, defaultPassword}})) + dispatch(userActions.closeLogin()) + dispatch(globalActions.remove({key: 'changePassword'})) + dispatch(globalActions.showDialog({name: 'changePassword', params: {username, defaultPassword}})) }, }) )(LoginForm) diff --git a/src/app/components/modules/Modals.jsx b/src/app/components/modules/Modals.jsx index 511499974..082ee78c6 100644 --- a/src/app/components/modules/Modals.jsx +++ b/src/app/components/modules/Modals.jsx @@ -2,16 +2,18 @@ import React from 'react'; import {connect} from 'react-redux'; import CloseButton from 'react-foundation-components/lib/global/close-button'; import Reveal from 'react-foundation-components/lib/global/reveal'; +import {NotificationStack} from 'react-notification'; +import {OrderedSet} from 'immutable'; + +import * as userActions from 'app/redux/UserReducer'; +import * as appActions from 'app/redux/AppReducer'; +import * as transactionActions from 'app/redux/TransactionReducer'; import LoginForm from 'app/components/modules/LoginForm'; import ConfirmTransactionForm from 'app/components/modules/ConfirmTransactionForm'; import Transfer from 'app/components/modules/Transfer'; import SignUp from 'app/components/modules/SignUp'; -import user from 'app/redux/User'; import Powerdown from 'app/components/modules/Powerdown'; -import tr from 'app/redux/Transaction'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; -import {NotificationStack} from 'react-notification'; -import {OrderedSet} from 'immutable'; import TermsAgree from 'app/components/modules/TermsAgree'; class Modals extends React.Component { @@ -100,29 +102,29 @@ export default connect( dispatch => ({ hideLogin: e => { if (e) e.preventDefault(); - dispatch(user.actions.hideLogin()) + dispatch(userActions.hideLogin()) }, hideConfirm: e => { if (e) e.preventDefault(); - dispatch(tr.actions.hideConfirm()) + dispatch(transactionActions.hideConfirm()) }, hideTransfer: e => { if (e) e.preventDefault(); - dispatch(user.actions.hideTransfer()) + dispatch(userActions.hideTransfer()) }, hidePowerdown: e => { if (e) e.preventDefault(); - dispatch(user.actions.hidePowerdown()) + dispatch(userActions.hidePowerdown()) }, hidePromotePost: e => { if (e) e.preventDefault(); - dispatch(user.actions.hidePromotePost()) + dispatch(userActions.hidePromotePost()) }, hideSignUp: e => { if (e) e.preventDefault(); - dispatch(user.actions.hideSignUp()) + dispatch(userActions.hideSignUp()) }, // example: addNotification: ({key, message}) => dispatch({type: 'ADD_NOTIFICATION', payload: {key, message}}), - removeNotification: (key) => dispatch({type: 'REMOVE_NOTIFICATION', payload: {key}}) + removeNotification: (key) => dispatch(appActions.removeNotification({key})) }) )(Modals) diff --git a/src/app/components/modules/Powerdown.jsx b/src/app/components/modules/Powerdown.jsx index 79dc36557..29ba5c160 100644 --- a/src/app/components/modules/Powerdown.jsx +++ b/src/app/components/modules/Powerdown.jsx @@ -1,11 +1,11 @@ import React from 'react'; import {connect} from 'react-redux' -import g from 'app/redux/GlobalReducer' -import reactForm from 'app/utils/ReactForm' import Slider from 'react-rangeslider'; -import transaction from 'app/redux/Transaction'; -import user from 'app/redux/User'; import tt from 'counterpart' +import reactForm from 'app/utils/ReactForm' +import * as globalActions from 'app/redux/GlobalReducer' +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as userActions from 'app/redux/UserReducer'; import {VEST_TICKER, LIQUID_TICKER, VESTING_TOKEN} from 'app/client_config' import {numberWithCommas, spToVestsf, vestsToSpf, vestsToSp, assetFloat} from 'app/utils/StateFunctions' @@ -157,19 +157,19 @@ export default connect( // mapDispatchToProps dispatch => ({ successCallback: () => { - dispatch(user.actions.hidePowerdown()) + dispatch(userActions.hidePowerdown()) }, powerDown: (e) => { e.preventDefault() const name = 'powerDown'; - dispatch(g.actions.showDialog({name})) + dispatch(globalActions.showDialog({name})) }, withdrawVesting: ({account, vesting_shares, errorCallback, successCallback}) => { const successCallbackWrapper = (...args) => { - dispatch({type: 'global/GET_STATE', payload: {url: `@${account}/transfers`}}) + dispatch(globalActions.getState({ url: `@${account}/transfers` })); return successCallback(...args) } - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'withdraw_vesting', operation: {account, vesting_shares}, errorCallback, diff --git a/src/app/components/modules/PromotePost.jsx b/src/app/components/modules/PromotePost.jsx index bee738d7d..c121b889d 100644 --- a/src/app/components/modules/PromotePost.jsx +++ b/src/app/components/modules/PromotePost.jsx @@ -1,7 +1,8 @@ import React, { PropTypes, Component } from 'react'; import {connect} from 'react-redux'; import ReactDOM from 'react-dom'; -import transaction from 'app/redux/Transaction'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import { DEBT_TOKEN, DEBT_TOKEN_SHORT, CURRENCY_SIGN, DEBT_TICKER} from 'app/client_config'; import tt from 'counterpart'; @@ -113,7 +114,7 @@ export default connect( dispatchSubmit: ({amount, asset, author, permlink, currentUser, onClose, errorCallback}) => { const username = currentUser.get('username') const successCallback = () => { - dispatch({type: 'global/GET_STATE', payload: {url: `@${username}/transfers`}}) // refresh transfer history + dispatch(globalActions.getState({ url: `@${username}/transfers` })); // refresh transfer history onClose() } const operation = { @@ -122,7 +123,7 @@ export default connect( memo: `@${author}/${permlink}`, __config: {successMessage: tt('promote_post_jsx.you_successfully_promoted_this_post') + '.'} } - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'transfer', operation, successCallback, diff --git a/src/app/components/modules/Settings.jsx b/src/app/components/modules/Settings.jsx index 3e875faa5..4def6b869 100644 --- a/src/app/components/modules/Settings.jsx +++ b/src/app/components/modules/Settings.jsx @@ -1,8 +1,9 @@ import React from 'react'; import {connect} from 'react-redux' -import user from 'app/redux/User'; import tt from 'counterpart'; -import transaction from 'app/redux/Transaction' +import * as userActions from 'app/redux/UserReducer'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as appActions from 'app/redux/AppReducer'; import o2j from 'shared/clash/object2json' import LoadingIndicator from 'app/components/elements/LoadingIndicator' import reactForm from 'app/utils/ReactForm' @@ -251,14 +252,14 @@ export default connect( // mapDispatchToProps dispatch => ({ changeLanguage: (language) => { - dispatch(user.actions.changeLanguage(language)) + dispatch(userActions.changeLanguage(language)) }, updateAccount: ({successCallback, errorCallback, ...operation}) => { const options = {type: 'account_update', operation, successCallback, errorCallback} - dispatch(transaction.actions.broadcastOperation(options)) + dispatch(transactionActions.broadcastOperation(options)) }, setUserPreferences: (payload) => { - dispatch({type: 'SET_USER_PREFERENCES', payload}) + dispatch(appActions.setUserPreferences(payload)); } }) )(Settings) diff --git a/src/app/components/modules/TopRightMenu.jsx b/src/app/components/modules/TopRightMenu.jsx index 933fb41db..6f5f185e5 100644 --- a/src/app/components/modules/TopRightMenu.jsx +++ b/src/app/components/modules/TopRightMenu.jsx @@ -1,15 +1,16 @@ import React from 'react'; import { Link } from 'react-router'; import {connect} from 'react-redux'; -import Icon from 'app/components/elements/Icon'; -import user from 'app/redux/User'; -import Userpic from 'app/components/elements/Userpic'; import { browserHistory } from 'react-router'; +import tt from 'counterpart'; import { LinkWithDropdown } from 'react-foundation-components/lib/global/dropdown'; +import Icon from 'app/components/elements/Icon'; +import * as userActions from 'app/redux/UserReducer'; +import * as appActions from 'app/redux/AppReducer'; +import Userpic from 'app/components/elements/Userpic'; import VerticalMenu from 'app/components/elements/VerticalMenu'; import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import NotifiCounter from 'app/components/elements/NotifiCounter'; -import tt from 'counterpart'; const defaultNavigate = (e) => { if (e.metaKey || e.ctrlKey) { @@ -138,15 +139,15 @@ export default connect( dispatch => ({ showLogin: e => { if (e) e.preventDefault(); - dispatch(user.actions.showLogin()) + dispatch(userActions.showLogin()) }, logout: e => { if (e) e.preventDefault(); - dispatch(user.actions.logout()) + dispatch(userActions.logout()) }, toggleNightmode: e => { if (e) e.preventDefault(); - dispatch({ type: 'TOGGLE_NIGHTMODE' }); + dispatch(appActions.toggleNightmode()); }, }) )(TopRightMenu); diff --git a/src/app/components/modules/Transfer.jsx b/src/app/components/modules/Transfer.jsx index 686a1e696..8640ecffe 100644 --- a/src/app/components/modules/Transfer.jsx +++ b/src/app/components/modules/Transfer.jsx @@ -2,8 +2,9 @@ import React, { PropTypes, Component } from 'react'; import ReactDOM from 'react-dom'; import reactForm from 'app/utils/ReactForm'; import {Map} from 'immutable'; -import transaction from 'app/redux/Transaction'; -import user from 'app/redux/User'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as userActions from 'app/redux/UserReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import runTests, {browserTests} from 'app/utils/BrowserTests' import {validate_account_name, validate_memo_field} from 'app/utils/ChainValidation'; @@ -293,11 +294,11 @@ export default connect( const username = currentUser.get('username'); const successCallback = () => { // refresh transfer history - dispatch({type: 'global/GET_STATE', payload: {url: `@${username}/transfers`}}); + dispatch(globalActions.getState({ url: `@${username}/transfers` })); if(/Savings Withdraw/.test(transferType)) { - dispatch({type: 'user/LOAD_SAVINGS_WITHDRAW', payload: {}}) + dispatch(userActions.loadSavingsWithdraw({})); } - dispatch(user.actions.hideTransfer()) + dispatch(userActions.hideTransfer()) }; const asset2 = toVesting ? 'STEEM' : asset; const operation = { @@ -309,7 +310,7 @@ export default connect( if(transferType === 'Savings Withdraw') operation.request_id = Math.floor((Date.now() / 1000) % 4294967295); - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: toVesting ? 'transfer_to_vesting' : ( transferType === 'Transfer to Account' ? 'transfer' : transferType === 'Transfer to Savings' ? 'transfer_to_savings' : diff --git a/src/app/components/modules/UserWallet.jsx b/src/app/components/modules/UserWallet.jsx index 76c4c5422..1a94c6613 100644 --- a/src/app/components/modules/UserWallet.jsx +++ b/src/app/components/modules/UserWallet.jsx @@ -2,7 +2,8 @@ import React from 'react'; import {connect} from 'react-redux' import {Link} from 'react-router' -import g from 'app/redux/GlobalReducer' +import tt from 'counterpart'; +import {List} from 'immutable'; import SavingsWithdrawHistory from 'app/components/elements/SavingsWithdrawHistory'; import TransferHistoryRow from 'app/components/cards/TransferHistoryRow'; import TransactionError from 'app/components/elements/TransactionError'; @@ -13,10 +14,9 @@ import WalletSubMenu from 'app/components/elements/WalletSubMenu' import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; import Tooltip from 'app/components/elements/Tooltip' import {FormattedHTMLMessage} from 'app/Translator'; -import tt from 'counterpart'; -import {List} from 'immutable' import { LIQUID_TOKEN, LIQUID_TICKER, DEBT_TOKENS, VESTING_TOKEN } from 'app/client_config'; -import transaction from 'app/redux/Transaction'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; const assetPrecision = 1000; @@ -422,7 +422,7 @@ export default connect( claimRewards: (account) => { const username = account.get('name') const successCallback = () => { - dispatch({type: 'global/GET_STATE', payload: {url: `@${username}/transfers`}}) + dispatch(globalActions.getState({ url: `@${username}/transfers` })); }; const operation = { @@ -432,7 +432,7 @@ export default connect( reward_vests: account.get('reward_vesting_balance') }; - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'claim_reward_balance', operation, successCallback, @@ -441,12 +441,12 @@ export default connect( convertToSteem: (e) => { e.preventDefault() const name = 'convertToSteem'; - dispatch(g.actions.showDialog({name})) + dispatch(globalActions.showDialog({name})) }, showChangePassword: (username) => { const name = 'changePassword'; - dispatch(g.actions.remove({key: name})); - dispatch(g.actions.showDialog({name, params: {username}})) + dispatch(globalActions.remove({key: name})); + dispatch(globalActions.showDialog({name, params: {username}})) }, }) )(UserWallet) diff --git a/src/app/components/pages/CreateAccount.jsx b/src/app/components/pages/CreateAccount.jsx index 5cebb74cb..9a9972a72 100644 --- a/src/app/components/pages/CreateAccount.jsx +++ b/src/app/components/pages/CreateAccount.jsx @@ -2,15 +2,16 @@ /*global $STM_csrf, $STM_Config */ import React from 'react'; import {connect} from 'react-redux'; +import { Link } from 'react-router'; +import {api} from '@steemit/steem-js'; + import LoadingIndicator from 'app/components/elements/LoadingIndicator'; -import user from 'app/redux/User'; import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; +import * as userActions from 'app/redux/UserReducer'; import {validate_account_name} from 'app/utils/ChainValidation'; import runTests from 'app/utils/BrowserTests'; import GeneratedPasswordInput from 'app/components/elements/GeneratedPasswordInput'; import {saveCords} from 'app/utils/ServerApiClient'; -import {api} from '@steemit/steem-js'; -import { Link } from 'react-router'; class CreateAccount extends React.Component { @@ -321,14 +322,14 @@ module.exports = { } }, dispatch => ({ - loginUser: (username, password) => dispatch(user.actions.usernamePasswordLogin({username, password, saveLogin: true})), + loginUser: (username, password) => dispatch(userActions.usernamePasswordLogin({username, password, saveLogin: true})), logout: e => { if (e) e.preventDefault(); - dispatch(user.actions.logout()) + dispatch(userActions.logout()) }, showTerms: e => { if (e) e.preventDefault(); - dispatch(user.actions.showTerms()) + dispatch(userActions.showTerms()) } }) )(CreateAccount) diff --git a/src/app/components/pages/Market.jsx b/src/app/components/pages/Market.jsx index fb273d5be..262985e24 100644 --- a/src/app/components/pages/Market.jsx +++ b/src/app/components/pages/Market.jsx @@ -3,8 +3,10 @@ import ReactDOM from 'react-dom'; import {connect} from 'react-redux'; //import Highcharts from 'highcharts'; -import transaction from 'app/redux/Transaction' -import TransactionError from 'app/components/elements/TransactionError' +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as appActions from 'app/redux/AppReducer'; +import * as marketActions from 'app/redux/MarketReducer'; +import TransactionError from 'app/components/elements/TransactionError'; import DepthChart from 'app/components/elements/DepthChart'; import Orderbook from "app/components/elements/Orderbook"; import OrderHistory from "app/components/elements/OrderHistory"; @@ -546,20 +548,20 @@ module.exports = { }, dispatch => ({ notify: (message) => { - dispatch({type: 'ADD_NOTIFICATION', payload: - {key: "mkt_" + Date.now(), - message: message, - dismissAfter: 5000} - }); + dispatch(appActions.addNotification({ + key: "mkt_" + Date.now(), + message, + dismissAfter: 5000, + })); }, reload: (username) => { console.log("Reload market state...") - dispatch({type: 'market/UPDATE_MARKET', payload: {username: username}}) + dispatch(marketActions.updateMarket({ username })); }, cancelOrder: (owner, orderid, successCallback) => { const confirm = tt('market_jsx.order_cancel_confirm', {order_id: orderid, user: owner}) const successMessage = tt('market_jsx.order_cancelled', {order_id: orderid}) - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'limit_order_cancel', operation: {owner, orderid/*, __config: {successMessage}*/}, confirm, @@ -588,7 +590,7 @@ module.exports = { warning = isSell ? tt('market_jsx.price_warning_below', warning_args) : tt('market_jsx.price_warning_above', warning_args); } const orderid = Math.floor(Date.now() / 1000) - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'limit_order_create', operation: {owner, amount_to_sell, min_to_receive, fill_or_kill, expiration, orderid}, //, //__config: {successMessage}}, diff --git a/src/app/components/pages/PickAccount.jsx b/src/app/components/pages/PickAccount.jsx index 583e6a9bb..7bf12d3cd 100644 --- a/src/app/components/pages/PickAccount.jsx +++ b/src/app/components/pages/PickAccount.jsx @@ -6,7 +6,8 @@ import Progress from 'react-foundation-components/lib/global/progress-bar'; import { Link } from 'react-router'; import classNames from 'classnames'; import {api} from '@steemit/steem-js'; -import user from 'app/redux/User'; + +import * as userActions from 'app/redux/UserReducer'; import {validate_account_name} from 'app/utils/ChainValidation'; import runTests from 'app/utils/BrowserTests'; import {PARAM_VIEW_MODE} from 'shared/constants'; @@ -239,10 +240,10 @@ module.exports = { } }, dispatch => ({ - loginUser: (username, password) => dispatch(user.actions.usernamePasswordLogin({username, password, saveLogin: true})), + loginUser: (username, password) => dispatch(userActions.usernamePasswordLogin({username, password, saveLogin: true})), logout: e => { if (e) e.preventDefault(); - dispatch(user.actions.logout()) + dispatch(userActions.logout()) } }) )(PickAccount) diff --git a/src/app/components/pages/PostPageNoCategory.jsx b/src/app/components/pages/PostPageNoCategory.jsx index 84b478e2a..72e5eefe3 100644 --- a/src/app/components/pages/PostPageNoCategory.jsx +++ b/src/app/components/pages/PostPageNoCategory.jsx @@ -1,8 +1,9 @@ import React from 'react'; +import { browserHistory } from 'react-router'; +import { connect } from 'react-redux'; +import { actions as fetchDataSagaActions } from 'app/redux/FetchDataSaga'; import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import SvgImage from 'app/components/elements/SvgImage'; -import { browserHistory } from 'react-router'; -import {connect} from 'react-redux'; class PostWrapper extends React.Component { @@ -61,7 +62,7 @@ const StoreWrapped = connect( }, dispatch => ({ getContent: (payload) => (new Promise((resolve, reject) => { - dispatch({type: 'GET_CONTENT', payload: {...payload, resolve, reject}}) + dispatch(fetchDataSagaActions.getContent({ ...payload, resolve, reject })); })) }) )(PostWrapper); diff --git a/src/app/components/pages/PostsIndex.jsx b/src/app/components/pages/PostsIndex.jsx index 7f9983713..7135c581e 100644 --- a/src/app/components/pages/PostsIndex.jsx +++ b/src/app/components/pages/PostsIndex.jsx @@ -1,20 +1,21 @@ /* eslint react/prop-types: 0 */ import React, {PropTypes} from 'react'; import {connect} from 'react-redux'; +import {Link} from 'react-router'; +import tt from 'counterpart'; +import { List } from 'immutable'; +import { actions as fetchDataSagaActions } from 'app/redux/FetchDataSaga'; import constants from 'app/redux/constants'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; import PostsList from 'app/components/cards/PostsList'; import {isFetchingOrRecentlyUpdated} from 'app/utils/StateFunctions'; -import {Link} from 'react-router'; import MarkNotificationRead from 'app/components/elements/MarkNotificationRead'; -import tt from 'counterpart'; -import Immutable from "immutable"; import Callout from 'app/components/elements/Callout'; // import SidebarStats from 'app/components/elements/SidebarStats'; import SidebarLinks from 'app/components/elements/SidebarLinks'; import SidebarNewUsers from 'app/components/elements/SidebarNewUsers'; -import Topics from './Topics'; import ArticleLayoutSelector from 'app/components/modules/ArticleLayoutSelector'; +import Topics from './Topics'; class PostsIndex extends React.Component { @@ -155,7 +156,7 @@ class PostsIndex extends React.Component { {(!fetching && (posts && !posts.size)) ? {emptyText} : { return { - requestData: (args) => dispatch({type: 'REQUEST_DATA', payload: args}), + requestData: args => dispatch(fetchDataSagaActions.requestData(args)), } } )(PostsIndex) diff --git a/src/app/components/pages/RecoverAccountStep2.jsx b/src/app/components/pages/RecoverAccountStep2.jsx index 085dec679..8e7f6f565 100644 --- a/src/app/components/pages/RecoverAccountStep2.jsx +++ b/src/app/components/pages/RecoverAccountStep2.jsx @@ -1,12 +1,15 @@ import React from 'react'; import {connect} from 'react-redux'; -import GeneratedPasswordInput from 'app/components/elements/GeneratedPasswordInput'; -import LoadingIndicator from 'app/components/elements/LoadingIndicator'; import tt from 'counterpart'; -import Callout from 'app/components/elements/Callout'; import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; import {api} from '@steemit/steem-js'; +import * as userActions from 'app/redux/UserReducer'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import GeneratedPasswordInput from 'app/components/elements/GeneratedPasswordInput'; +import LoadingIndicator from 'app/components/elements/LoadingIndicator'; +import Callout from 'app/components/elements/Callout'; + function passwordToOwnerPubKey(account_name, password) { let pub_key; try { @@ -189,10 +192,8 @@ module.exports = { recoverAccount: ( account_to_recover, old_password, new_password, onError, onSuccess ) => { - dispatch({type: 'transaction/RECOVER_ACCOUNT', - payload: {account_to_recover, old_password, new_password, onError, onSuccess} - }) - dispatch({type: 'user/LOGOUT'}) + dispatch(transactionActions.recoverAccount({ account_to_recover, old_password, new_password, onError, onSuccess })); + dispatch(userActions.logout()); }, }) )(RecoverAccountStep2) diff --git a/src/app/components/pages/UserProfile.jsx b/src/app/components/pages/UserProfile.jsx index f6fe1bf28..b0ab26792 100644 --- a/src/app/components/pages/UserProfile.jsx +++ b/src/app/components/pages/UserProfile.jsx @@ -4,8 +4,9 @@ import { Link } from 'react-router'; import {connect} from 'react-redux'; import { browserHistory } from 'react-router'; import classnames from 'classnames'; -import transaction from 'app/redux/Transaction'; -import user from 'app/redux/User'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as userActions from 'app/redux/UserReducer'; +import { actions as fetchDataSagaActions } from 'app/redux/FetchDataSaga'; import Icon from 'app/components/elements/Icon' import UserKeys from 'app/components/elements/UserKeys'; import PasswordReset from 'app/components/elements/PasswordReset'; @@ -519,31 +520,31 @@ module.exports = { }; }, dispatch => ({ - login: () => {dispatch(user.actions.showLogin())}, - clearTransferDefaults: () => {dispatch(user.actions.clearTransferDefaults())}, + login: () => {dispatch(userActions.showLogin())}, + clearTransferDefaults: () => {dispatch(userActions.clearTransferDefaults())}, showTransfer: (transferDefaults) => { - dispatch(user.actions.setTransferDefaults(transferDefaults)) - dispatch(user.actions.showTransfer()) + dispatch(userActions.setTransferDefaults(transferDefaults)) + dispatch(userActions.showTransfer()) }, - clearPowerdownDefaults: () => {dispatch(user.actions.clearPowerdownDefaults())}, + clearPowerdownDefaults: () => {dispatch(userActions.clearPowerdownDefaults())}, showPowerdown: (powerdownDefaults) => { console.log('power down defaults:', powerdownDefaults) - dispatch(user.actions.setPowerdownDefaults(powerdownDefaults)) - dispatch(user.actions.showPowerdown()) + dispatch(userActions.setPowerdownDefaults(powerdownDefaults)) + dispatch(userActions.showPowerdown()) }, withdrawVesting: ({account, vesting_shares, errorCallback, successCallback}) => { const successCallbackWrapper = (...args) => { - dispatch({type: 'global/GET_STATE', payload: {url: `@${account}/transfers`}}) + dispatch(globalActions.getState({ url: `@${account}/transfers` })); return successCallback(...args) } - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'withdraw_vesting', operation: {account, vesting_shares}, errorCallback, successCallback: successCallbackWrapper, })) }, - requestData: (args) => dispatch({type: 'REQUEST_DATA', payload: args}), + requestData: args => dispatch(fetchDataSagaActions.requestData(args)), }) )(UserProfile) }; diff --git a/src/app/components/pages/Witnesses.jsx b/src/app/components/pages/Witnesses.jsx index 9b2ca71a3..c7b711e9e 100644 --- a/src/app/components/pages/Witnesses.jsx +++ b/src/app/components/pages/Witnesses.jsx @@ -3,10 +3,10 @@ import {connect} from 'react-redux'; import { Link } from 'react-router'; import links from 'app/utils/Links' import Icon from 'app/components/elements/Icon'; -import transaction from 'app/redux/Transaction' +import * as transactionActions from 'app/redux/TransactionReducer'; import ByteBuffer from 'bytebuffer' import {is} from 'immutable' -import g from 'app/redux/GlobalReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; import tt from 'counterpart'; const Long = ByteBuffer.Long @@ -224,18 +224,18 @@ module.exports = { (dispatch) => { return { accountWitnessVote: (username, witness, approve) => { - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'account_witness_vote', operation: {account: username, witness, approve}, })) }, accountWitnessProxy: (account, proxy, stateCallback) => { - dispatch(transaction.actions.broadcastOperation({ + dispatch(transactionActions.broadcastOperation({ type: 'account_witness_proxy', operation: {account, proxy}, confirm: proxy.length ? "Set proxy to: " + proxy : "You are about to remove your proxy.", successCallback: () => { - dispatch(g.actions.updateAccountWitnessProxy({account, proxy})); + dispatch(globalActions.updateAccountWitnessProxy({account, proxy})); stateCallback({proxyFailed: false, proxy: ""}); }, errorCallback: (e) => { diff --git a/src/app/redux/AppReducer.js b/src/app/redux/AppReducer.js index cb3e3ed8b..db52827de 100644 --- a/src/app/redux/AppReducer.js +++ b/src/app/redux/AppReducer.js @@ -1,13 +1,23 @@ import {Map, OrderedMap} from 'immutable'; import tt from 'counterpart'; +// Action constants + +const STEEM_API_ERROR = 'app/STEEM_API_ERROR'; +const FETCH_DATA_BEGIN = 'app/FETCH_DATA_BEGIN'; +const FETCH_DATA_END = 'app/FETCH_DATA_END'; +const ADD_NOTIFICATION = 'app/ADD_NOTIFICATION'; +const REMOVE_NOTIFICATION = 'app/REMOVE_NOTIFICATION'; +const UPDATE_NOTIFICOUNTERS = 'app/UPDATE_NOTIFICOUNTERS'; +export const SET_USER_PREFERENCES = 'app/SET_USER_PREFERENCES'; +export const TOGGLE_NIGHTMODE = 'app/TOGGLE_NIGHTMODE'; +export const TOGGLE_BLOGMODE = 'app/TOGGLE_BLOGMODE'; + const defaultState = Map({ - requests: {}, loading: false, error: '', location: {}, notifications: null, - ignoredLoadingRequestCount: 0, notificounters: Map({ total: 0, feed: 0, @@ -24,55 +34,93 @@ const defaultState = Map({ user_preferences: Map({ locale: null, nsfwPref: 'warn', - theme: 'light', + nightmode: false, blogmode: false, currency: 'USD' - }) + }), }); export default function reducer(state = defaultState, action) { - if (action.type === '@@router/LOCATION_CHANGE') { - return state.set('location', {pathname: action.payload.pathname}); - } - if (action.type === 'STEEM_API_ERROR') { - return state.set('error', action.error).set('loading', false); - } - let res = state; - if (action.type === 'FETCH_DATA_BEGIN') { - res = state.set('loading', true); - } - if (action.type === 'FETCH_DATA_END') { - res = state.set('loading', false); - } - if (action.type === 'ADD_NOTIFICATION') { - const n = { - action: tt('g.dismiss'), - dismissAfter: 10000, - ...action.payload - }; - res = res.update('notifications', s => { - return s ? s.set(n.key, n) : OrderedMap({[n.key]: n}); - }); - } - if (action.type === 'REMOVE_NOTIFICATION') { - res = res.update('notifications', s => s.delete(action.payload.key)); - } - if (action.type === 'UPDATE_NOTIFICOUNTERS' && action.payload) { - const nc = action.payload; - if (nc.follow > 0) { - nc.total -= nc.follow; - nc.follow = 0; + switch (action.type) { + case '@@router/LOCATION_CHANGE': + return state.set('location', { pathname: action.payload.pathname }); + case STEEM_API_ERROR: + return state.set('error', action.error).set('loading', false); + case FETCH_DATA_BEGIN: + return state.set('loading', true); + case FETCH_DATA_END: + return state.set('loading', false); + case ADD_NOTIFICATION: { + const n = { + action: tt('g.dismiss'), + dismissAfter: 10000, + ...action.payload + }; + return state.update('notifications', (s) => { + return s ? s.set(n.key, n) : OrderedMap({[n.key]: n}); + }); } - res = res.set('notificounters', Map(nc)); - } - if (action.type === 'SET_USER_PREFERENCES') { - res = res.set('user_preferences', Map(action.payload)); - } - if (action.type === 'TOGGLE_NIGHTMODE') { - res = res.setIn(['user_preferences', 'nightmode'], !res.getIn(['user_preferences', 'nightmode'])); - } - if (action.type === 'TOGGLE_BLOGMODE') { - res = res.setIn(['user_preferences', 'blogmode'], !res.getIn(['user_preferences', 'blogmode'])); + case REMOVE_NOTIFICATION: + return state.update('notifications', s => s.delete(action.payload.key)); + case UPDATE_NOTIFICOUNTERS: { + if (action.payload) { + const nc = action.payload; + if (nc.follow > 0) { + nc.total -= nc.follow; + nc.follow = 0; + } + return state.set('notificounters', Map(nc)); + } + return state; + } + case SET_USER_PREFERENCES: + return state.set('user_preferences', Map(action.payload)); + case TOGGLE_NIGHTMODE: + return state.setIn(['user_preferences', 'nightmode'], !state.getIn(['user_preferences', 'nightmode'])); + case TOGGLE_BLOGMODE: + return state.setIn(['user_preferences', 'blogmode'], !state.getIn(['user_preferences', 'blogmode'])); + default: + return state; } - return res; } + +export const steemApiError = (error) => ({ + type: STEEM_API_ERROR, + error, +}); + +export const fetchDataBegin = () => ({ + type: FETCH_DATA_BEGIN, +}); + +export const fetchDataEnd = () => ({ + type: FETCH_DATA_END, +}); + +export const addNotification = (payload) => ({ + type: ADD_NOTIFICATION, + payload, +}); + +export const removeNotification = (payload) => ({ + type: REMOVE_NOTIFICATION, + payload, +}); + +export const updateNotificounters = (payload) => ({ + type: UPDATE_NOTIFICOUNTERS, + payload, +}); + +export const setUserPreferences = (payload) => ({ + type: SET_USER_PREFERENCES, + payload, +}); + +export const toggleNightmode = () => ({ + type: TOGGLE_NIGHTMODE, +}); + +export const toggleBlogmode = () => ({ + type: TOGGLE_BLOGMODE, +}); diff --git a/src/app/redux/AuthSaga.js b/src/app/redux/AuthSaga.js index e80366d96..bc56fe844 100644 --- a/src/app/redux/AuthSaga.js +++ b/src/app/redux/AuthSaga.js @@ -1,10 +1,11 @@ import {takeEvery} from 'redux-saga'; import {call, put, select} from 'redux-saga/effects'; import {Set, Map, fromJS, List} from 'immutable' -import user from 'app/redux/User' -import {getAccount} from 'app/redux/SagaShared' -import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; import {api} from '@steemit/steem-js'; +import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; + +import {getAccount} from 'app/redux/SagaShared'; +import * as userActions from 'app/redux/UserReducer'; // operations that require only posting authority const postingOps = Set(`vote, comment, delete_comment, custom_json, claim_reward_balance`.trim().split(/,\s*/)) @@ -43,7 +44,7 @@ export function* accountAuthLookup({payload: {account, private_keys, login_owner } const accountName = account.get('name') const pub_keys_used = {posting: toPub(posting), active: toPub(active), owner: login_owner_pubkey}; - yield put(user.actions.setAuthority({accountName, auth, pub_keys_used})) + yield put(userActions.setAuthority({ accountName, auth, pub_keys_used })); } /** diff --git a/src/app/redux/FetchDataSaga.js b/src/app/redux/FetchDataSaga.js index d15a83805..d3076ef58 100644 --- a/src/app/redux/FetchDataSaga.js +++ b/src/app/redux/FetchDataSaga.js @@ -2,19 +2,24 @@ import {takeLatest, takeEvery} from 'redux-saga'; import {call, put, select, fork} from 'redux-saga/effects'; import {loadFollows, fetchFollowCount} from 'app/redux/FollowSaga'; import {getContent} from 'app/redux/SagaShared'; -import GlobalReducer from './GlobalReducer'; +import * as globalActions from './GlobalReducer'; +import * as appActions from './AppReducer'; import constants from './constants'; import {fromJS, Map} from 'immutable' import {api} from '@steemit/steem-js'; +const REQUEST_DATA = 'fetchDataSaga/REQUEST_DATA'; +const GET_CONTENT = 'fetchDataSaga/GET_CONTENT'; +const FETCH_STATE = 'fetchDataSaga/FETCH_STATE'; + export const fetchDataWatches = [watchLocationChange, watchDataRequests, watchFetchJsonRequests, watchFetchState, watchGetContent]; export function* watchDataRequests() { - yield* takeLatest('REQUEST_DATA', fetchData); + yield* takeLatest(REQUEST_DATA, fetchData); } export function* watchGetContent() { - yield* takeEvery('GET_CONTENT', getContentCaller); + yield* takeEvery(GET_CONTENT, getContentCaller); } export function* getContentCaller(action) { @@ -46,15 +51,15 @@ export function* fetchState(location_change_action) { if (url.indexOf("/curation-rewards") !== -1) url = url.replace("/curation-rewards", "/transfers"); if (url.indexOf("/author-rewards") !== -1) url = url.replace("/author-rewards", "/transfers"); - yield put({type: 'FETCH_DATA_BEGIN'}); + yield put(appActions.fetchDataBegin()); try { const state = yield call([api, api.getStateAsync], url) - yield put(GlobalReducer.actions.receiveState(state)); + yield put(globalActions.receiveState(state)); } catch (error) { console.error('~~ Saga fetchState error ~~>', url, error); - yield put({type: 'global/STEEM_API_ERROR', error: error.message}); + yield put(appActions.steemApiError(error.message)); } - yield put({type: 'FETCH_DATA_END'}); + yield put(appActions.fetchDataEnd()); } export function* watchLocationChange() { @@ -62,7 +67,7 @@ export function* watchLocationChange() { } export function* watchFetchState() { - yield* takeLatest('FETCH_STATE', fetchState); + yield* takeLatest(FETCH_STATE, fetchState); } export function* fetchData(action) { @@ -71,7 +76,7 @@ export function* fetchData(action) { if( !category ) category = ""; category = category.toLowerCase(); - yield put({type: 'global/FETCHING_DATA', payload: {order, category}}); + yield put(globalActions.fetchingData({order, category})); let call_name, args; if (order === 'trending') { call_name = 'getDiscussionsByTrendingAsync'; @@ -188,15 +193,15 @@ export function* fetchData(action) { start_author: author, start_permlink: permlink}]; } - yield put({type: 'FETCH_DATA_BEGIN'}); + yield put(appActions.fetchDataBegin()); try { const data = yield call([api, api[call_name]], ...args); - yield put(GlobalReducer.actions.receiveData({data, order, category, author, permlink, accountname})); + yield put(globalActions.receiveData({data, order, category, author, permlink, accountname})); } catch (error) { console.error('~~ Saga fetchData error ~~>', call_name, args, error); - yield put({type: 'global/STEEM_API_ERROR', error: error.message}); + yield put(appActions.steemApiError(error.message)); } - yield put({type: 'FETCH_DATA_END'}); + yield put(appActions.fetchDataEnd()); } // export function* watchMetaRequests() { @@ -237,9 +242,9 @@ export function* fetchMeta({payload: {id, link}}) { if(!meta.image) { meta.image = meta['twitter:image:src'] } - yield put(GlobalReducer.actions.receiveMeta({id, meta})) + yield put(globalActions.receiveMeta({id, meta})) } catch(error) { - yield put(GlobalReducer.actions.receiveMeta({id, meta: {error}})) + yield put(globalActions.receiveMeta({id, meta: {error}})) } } @@ -265,9 +270,27 @@ function* fetchJson({payload: {id, url, body, successCallback, skipLoading = fal let result = yield skipLoading ? fetch(url, payload) : call(fetch, url, payload) result = yield result.json() if(successCallback) result = successCallback(result) - yield put(GlobalReducer.actions.fetchJsonResult({id, result})) + yield put(globalActions.fetchJsonResult({id, result})) } catch(error) { console.error('fetchJson', error) - yield put(GlobalReducer.actions.fetchJsonResult({id, error})) + yield put(globalActions.fetchJsonResult({id, error})) } } + +// Action creators +export const actions = { + requestData: (payload) => ({ + type: REQUEST_DATA, + payload, + }), + + getContent: (payload) => ({ + type: GET_CONTENT, + payload, + }), + + fetchState: (payload) => ({ + type: FETCH_STATE, + payload, + }), +}; diff --git a/src/app/redux/FollowSaga.js b/src/app/redux/FollowSaga.js index 835a573fb..3c3051af2 100644 --- a/src/app/redux/FollowSaga.js +++ b/src/app/redux/FollowSaga.js @@ -2,6 +2,8 @@ import {fromJS, Map, Set} from 'immutable' import {call, put, select} from 'redux-saga/effects'; import {api} from '@steemit/steem-js'; +import * as globalActions from 'app/redux/GlobalReducer'; + /** This loadFollows both 'blog' and 'ignore' */ @@ -9,14 +11,12 @@ import {api} from '@steemit/steem-js'; //fetch for follow/following count export function* fetchFollowCount(account) { const counts = yield call([api, api.getFollowCountAsync], account) - yield put({ - type: 'global/UPDATE', - payload: { - key: ['follow_count', account], - updater: m => m.mergeDeep({ - follower_count: counts.follower_count, - following_count: counts.following_count}) - }}) + yield put(globalActions.update({ + key: ['follow_count', account], + updater: m => m.mergeDeep({ + follower_count: counts.follower_count, + following_count: counts.following_count}) + })); } // Test limit with 2 (not 1, infinate looping) @@ -34,13 +34,11 @@ export function* loadFollows(method, account, type, force = false) { } } - yield put({ - type: 'global/UPDATE', - payload: { - key: ['follow', method, account], - notSet: Map(), - updater: m => m.set(type + '_loading', true), - }}) + yield put(globalActions.update({ + key: ['follow', method, account], + notSet: Map(), + updater: m => m.set(type + '_loading', true), + })); yield loadFollowsLoop(method, account, type) } @@ -53,7 +51,7 @@ function* loadFollowsLoop(method, account, type, start = '', limit = 100) { let cnt = 0 let lastAccountName = null - yield put({type: 'global/UPDATE', payload: { + yield put(globalActions.update({ key: ['follow_inprogress', method, account], notSet: Map(), updater: (m) => { @@ -71,7 +69,7 @@ function* loadFollowsLoop(method, account, type, start = '', limit = 100) { }) return m.asImmutable() } - }}) + })) if(cnt === limit) { // This is paging each block of up to limit results @@ -79,7 +77,7 @@ function* loadFollowsLoop(method, account, type, start = '', limit = 100) { } else { // This condition happens only once at the very end of the list. // Every account has a different followers and following list for: blog, ignore - yield put({type: 'global/UPDATE', payload: { + yield put(globalActions.update({ key: [], updater: (m) => { m = m.asMutable() @@ -94,6 +92,6 @@ function* loadFollowsLoop(method, account, type, start = '', limit = 100) { })) return m.asImmutable() } - }}) + })); } } diff --git a/src/app/redux/GlobalReducer.js b/src/app/redux/GlobalReducer.js index 9d91a4cf2..5451aca62 100644 --- a/src/app/redux/GlobalReducer.js +++ b/src/app/redux/GlobalReducer.js @@ -1,302 +1,424 @@ import {Map, Set, List, fromJS, Iterable} from 'immutable'; -import createModule from 'redux-modules'; import {emptyContent} from 'app/redux/EmptyState'; +import {contentStats} from 'app/utils/StateFunctions'; import constants from './constants'; -import {contentStats} from 'app/utils/StateFunctions' - -const emptyContentMap = Map(emptyContent) - -export default createModule({ - name: 'global', - initialState: Map({status: {}}), - transformations: [ - { - action: 'SET_COLLAPSED', - reducer: (state, action) => { - return state.withMutations(map => { - map.updateIn(['content', action.payload.post], value => { - value.merge(Map({collapsed: action.payload.collapsed})); - }); + +const emptyContentMap = Map(emptyContent); + +const defaultState = Map({status: {}}); + +// Action constants +const RECEIVE_STATE = 'global/RECEIVE_STATE'; +const SET_COLLAPSED = 'global/SET_COLLAPSED'; +const RECEIVE_ACCOUNT = 'global/RECEIVE_ACCOUNT'; +const RECEIVE_COMMENT = 'global/RECEIVE_COMMENT'; +const RECEIVE_CONTENT = 'global/RECEIVE_CONTENT'; +const LINK_REPLY = 'global/LINK_REPLY'; +const UPDATE_ACCOUNT_WITNESS_VOTE = 'global/UPDATE_ACCOUNT_WITNESS_VOTE'; +const UPDATE_ACCOUNT_WITNESS_PROXY = 'global/UPDATE_ACCOUNT_WITNESS_PROXY'; +const DELETE_CONTENT = 'global/DELETE_CONTENT'; +const VOTED = 'global/VOTED'; +const FETCHING_DATA = 'global/FETCHING_DATA'; +const RECEIVE_DATA = 'global/RECEIVE_DATA'; +const RECEIVE_RECENT_POSTS = 'global/RECEIVE_RECENT_POSTS'; +const REQUEST_META = 'global/REQUEST_META'; +const RECEIVE_META = 'global/RECEIVE_META'; +const SET = 'global/SET'; +const REMOVE = 'global/REMOVE'; +const UPDATE = 'global/UPDATE'; +const SET_META_DATA = 'global/SET_META_DATA'; +const CLEAR_META = 'global/CLEAR_META'; +const CLEAR_META_ELEMENT = 'global/CLEAR_META_ELEMENT'; +const FETCH_JSON = 'global/FETCH_JSON'; +const FETCH_JSON_RESULT = 'global/FETCH_JSON_RESULT'; +const SHOW_DIALOG = 'global/SHOW_DIALOG'; +const HIDE_DIALOG = 'global/HIDE_DIALOG'; +// Saga-related: +export const GET_STATE = 'global/GET_STATE'; + +export default function reducer(state = defaultState, action) { + const payload = action.payload; + + switch (action.type) { + case SET_COLLAPSED: { + return state.withMutations(map => { + map.updateIn(['content', payload.post], value => { + value.merge(Map({collapsed: payload.collapsed})); }); - } - }, - { - action: 'RECEIVE_STATE', - reducer: (state, action) => { - let payload = fromJS(action.payload) - if(payload.has('content')) { - const content = payload.get('content').withMutations(c => { - c.forEach((cc, key) => { - cc = emptyContentMap.mergeDeep(cc) - const stats = fromJS(contentStats(cc)) - c.setIn([key, 'stats'], stats) - }) + }); + } + + case RECEIVE_STATE: { + let new_state = fromJS(payload) + if(new_state.has('content')) { + const content = new_state.get('content').withMutations(c => { + c.forEach((cc, key) => { + cc = emptyContentMap.mergeDeep(cc) + const stats = fromJS(contentStats(cc)) + c.setIn([key, 'stats'], stats) }) - payload = payload.set('content', content) - } - // console.log('state.mergeDeep(action.payload).toJS(), action.payload', state.mergeDeep(action.payload).toJS(), action.payload) - return state.mergeDeep(payload); - } - }, - { - action: 'RECEIVE_ACCOUNT', - reducer: (state, {payload: {account}}) => { - account = fromJS(account, (key, value) => { - if (key === 'witness_votes') return value.toSet() - const isIndexed = Iterable.isIndexed(value); - return isIndexed ? value.toList() : value.toOrderedMap(); }) - // Merging accounts: A get_state will provide a very full account but a get_accounts will provide a smaller version - return state.updateIn(['accounts', account.get('name')], Map(), a => a.mergeDeep(account)) - } - }, - { - action: 'RECEIVE_COMMENT', - reducer: (state, {payload: op}) => { - const {author, permlink, parent_author = '', parent_permlink = '', title = '', body} = op - const key = author + '/' + permlink - - let updatedState = state.updateIn(['content', key], Map(emptyContent), r => r.merge({ - author, permlink, parent_author, parent_permlink, - title: title.toString('utf-8'), - body: body.toString('utf-8'), - })) - // console.log('updatedState content', updatedState.getIn(['content', key]).toJS()) - - if (parent_author !== '' && parent_permlink !== '') { - const parent_key = parent_author + '/' + parent_permlink - updatedState = updatedState.updateIn(['content', parent_key, 'replies'], List(), r => r.insert(0, key)) - const children = updatedState.getIn(['content', parent_key, 'replies'], List()).size; - updatedState = updatedState.updateIn(['content', parent_key, 'children'], 0, r => children) - // console.log('updatedState parent', updatedState.toJS()) - } - return updatedState + new_state = new_state.set('content', content) } - }, - { - action: 'RECEIVE_CONTENT', - reducer: (state, {payload: {content}}) => { - // console.log('GlobalReducer -- RECEIVE_CONTENT content', content) - content = fromJS(content) - const key = content.get('author') + '/' + content.get('permlink') - return state.updateIn(['content', key], Map(), c => { - c = emptyContentMap.mergeDeep(c) - c = c.delete('active_votes') - c = c.mergeDeep(content) - c = c.set('stats', fromJS(contentStats(c))) - return c - }) - } - }, - { // works... - action: 'LINK_REPLY', - reducer: (state, {payload: op}) => { - const {author, permlink, parent_author = '', parent_permlink = ''} = op - if (parent_author === '' || parent_permlink === '') return state - const key = author + '/' + permlink - const parent_key = parent_author + '/' + parent_permlink - // Add key if not exist - let updatedState = state.updateIn(['content', parent_key, 'replies'], List(), - l => (l.findIndex(i => i === key) === -1 ? l.push(key) : l)) + return state.mergeDeep(new_state); + } + + case RECEIVE_ACCOUNT: { + const account = fromJS(payload.account, (key, value) => { + if (key === 'witness_votes') return value.toSet() + const isIndexed = Iterable.isIndexed(value); + return isIndexed ? value.toList() : value.toOrderedMap(); + }) + // Merging accounts: A get_state will provide a very full account but a get_accounts will provide a smaller version + return state.updateIn(['accounts', account.get('name')], Map(), a => a.mergeDeep(account)); + } + + case RECEIVE_COMMENT: { + const {author, permlink, parent_author = '', parent_permlink = '', title = '', body} = payload.op; + const key = author + '/' + permlink; + let updatedState = state.updateIn(['content', key], Map(emptyContent), r => r.merge({ + author, permlink, parent_author, parent_permlink, + title: title.toString('utf-8'), + body: body.toString('utf-8'), + })); + if (parent_author !== '' && parent_permlink !== '') { + const parent_key = parent_author + '/' + parent_permlink; + updatedState = updatedState.updateIn(['content', parent_key, 'replies'], List(), r => r.insert(0, key)); const children = updatedState.getIn(['content', parent_key, 'replies'], List()).size; - updatedState = updatedState.updateIn(['content', parent_key, 'children'], 0, r => children) - return updatedState; - } - }, - { // works... - action: 'UPDATE_ACCOUNT_WITNESS_VOTE', - reducer: (state, {payload: {account, witness, approve}}) => - state.updateIn(['accounts', account, 'witness_votes'], Set(), - votes => (approve ? Set(votes).add(witness) : Set(votes).remove(witness))) - }, - { // works... - action: 'UPDATE_ACCOUNT_WITNESS_PROXY', - reducer: (state, {payload: {account, proxy}}) => - state.setIn(['accounts', account, 'proxy'], proxy) - }, - { - action: 'DELETE_CONTENT', - reducer: (state, {payload: {author, permlink}}) => { - const key = author + '/' + permlink - const content = state.getIn(['content', key]) - const parent_author = content.get('parent_author') || '' - const parent_permlink = content.get('parent_permlink') || '' - let updatedState = state.deleteIn(['content', key]) - if (parent_author !== '' && parent_permlink !== '') { - const parent_key = parent_author + '/' + parent_permlink - updatedState = updatedState.updateIn(['content', parent_key, 'replies'], - List(), r => r.filter(i => i !== key)) - } - return updatedState + updatedState = updatedState.updateIn(['content', parent_key, 'children'], 0, () => children); } - }, - { - action: 'VOTED', - reducer: (state, {payload: {username, author, permlink, weight}}) => { - const key = ['content', author + '/' + permlink, 'active_votes'] - let active_votes = state.getIn(key, List()) - const idx = active_votes.findIndex(v => v.get('voter') === username) - // steemd flips weight into percent - if(idx === -1) - active_votes = active_votes.push(Map({voter: username, percent: weight})); - else { - active_votes = active_votes.set(idx, Map({voter: username, percent: weight})); - } - state.setIn(key, active_votes); - return state; + return updatedState; + } + + case RECEIVE_CONTENT: { + const content = fromJS(payload.content) + const key = content.get('author') + '/' + content.get('permlink') + return state.updateIn(['content', key], Map(), c => { + c = emptyContentMap.mergeDeep(c) + c = c.delete('active_votes') + c = c.mergeDeep(content) + c = c.set('stats', fromJS(contentStats(c))) + return c + }); + } + + case LINK_REPLY: { + const {author, permlink, parent_author = '', parent_permlink = ''} = payload; + if (parent_author === '' || parent_permlink === '') return state; + const key = author + '/' + permlink; + const parent_key = parent_author + '/' + parent_permlink; + // Add key if not exist + let updatedState = state.updateIn(['content', parent_key, 'replies'], List(), + l => (l.findIndex(i => i === key) === -1 ? l.push(key) : l)) + const children = updatedState.getIn(['content', parent_key, 'replies'], List()).size; + updatedState = updatedState.updateIn(['content', parent_key, 'children'], 0, () => children); + return updatedState; + } + + case UPDATE_ACCOUNT_WITNESS_VOTE: { + const {account, witness, approve} = payload; + return state.updateIn(['accounts', account, 'witness_votes'], Set(), + votes => (approve ? Set(votes).add(witness) : Set(votes).remove(witness))); + } + + case UPDATE_ACCOUNT_WITNESS_PROXY: { + const {account, proxy} = payload; + return state.setIn(['accounts', account, 'proxy'], proxy); + } + + case DELETE_CONTENT: { + const {author, permlink} = payload; + const key = author + '/' + permlink + const content = state.getIn(['content', key]) + const parent_author = content.get('parent_author') || '' + const parent_permlink = content.get('parent_permlink') || '' + let updatedState = state.deleteIn(['content', key]) + if (parent_author !== '' && parent_permlink !== '') { + const parent_key = parent_author + '/' + parent_permlink + updatedState = updatedState.updateIn(['content', parent_key, 'replies'], + List(), r => r.filter(i => i !== key)) } - }, - { - action: 'FETCHING_DATA', - reducer: (state, {payload: {order, category}}) => { - const new_state = state.updateIn(['status', category || '', order], () => { - return {fetching: true}; - }); - return new_state; + return updatedState; + } + + case VOTED: { + const {username, author, permlink, weight} = payload; + const key = ['content', author + '/' + permlink, 'active_votes']; + let active_votes = state.getIn(key, List()); + const idx = active_votes.findIndex(v => v.get('voter') === username); + // steemd flips weight into percent + if(idx === -1) { + active_votes = active_votes.push(Map({voter: username, percent: weight})); + } else { + active_votes = active_votes.set(idx, Map({voter: username, percent: weight})); } - }, - { - action: 'RECEIVE_DATA', - reducer: (state, {payload: {data, order, category, author, accountname, /*permlink*/}}) => { - // console.log('-- RECEIVE_DATA reducer -->', order, category, author, permlink, data); - // console.log('-- RECEIVE_DATA state -->', state.toJS()); - let new_state; - if (order === 'by_author' || order === 'by_feed' || order === 'by_comments' || order === 'by_replies') { - // category is either "blog", "feed", "comments", or "recent_replies" (respectively) -- and all posts are keyed under current profile - const key = ['accounts', accountname, category] - new_state = state.updateIn(key, List(), list => { - return list.withMutations(posts => { - data.forEach(value => { - const key2 = `${value.author}/${value.permlink}` - if (!posts.includes(key2)) posts.push(key2); - }); - }); - }); - } else { - new_state = state.updateIn(['discussion_idx', category || '', order], list => { - return list.withMutations(posts => { - data.forEach(value => { - const entry = `${value.author}/${value.permlink}`; - if (!posts.includes(entry)) posts.push(entry); - }); - }); - }); - } - new_state = new_state.updateIn(['content'], content => { - return content.withMutations(map => { + state.setIn(key, active_votes); + return state; + } + + case FETCHING_DATA: { + const {order, category} = payload; + const new_state = state.updateIn(['status', category || '', order], () => { + return {fetching: true}; + }); + return new_state; + } + + case RECEIVE_DATA: { + const {data, order, category, accountname} = payload; + let new_state; + if (order === 'by_author' || order === 'by_feed' || order === 'by_comments' || order === 'by_replies') { + // category is either "blog", "feed", "comments", or "recent_replies" (respectively) -- and all posts are keyed under current profile + const key = ['accounts', accountname, category] + new_state = state.updateIn(key, List(), list => { + return list.withMutations(posts => { data.forEach(value => { - // console.log('GlobalReducer -- RECEIVE_DATA', value) - const key = `${value.author}/${value.permlink}`; - value = fromJS(value) - value = value.set('stats', fromJS(contentStats(value))) - map.set(key, value); + const key2 = `${value.author}/${value.permlink}` + if (!posts.includes(key2)) posts.push(key2); }); }); }); - new_state = new_state.updateIn(['status', category || '', order], () => { - if (data.length < constants.FETCH_DATA_BATCH_SIZE) { - return {fetching: false, last_fetch: new Date()}; - } - return {fetching: false}; - }); - // console.log('-- new_state -->', new_state.toJS()); - return new_state; - } - }, - { - action: 'RECEIVE_RECENT_POSTS', - reducer: (state, {payload: {data}}) => { - // console.log('-- RECEIVE_RECENT_POSTS state -->', state.toJS()); - // console.log('-- RECEIVE_RECENT_POSTS reducer -->', data); - let new_state = state.updateIn(['discussion_idx', '', 'created'], list => { - if (!list) list = List(); + } else { + new_state = state.updateIn(['discussion_idx', category || '', order], list => { return list.withMutations(posts => { data.forEach(value => { const entry = `${value.author}/${value.permlink}`; - if (!posts.includes(entry)) posts.unshift(entry); + if (!posts.includes(entry)) posts.push(entry); }); }); }); - new_state = new_state.updateIn(['content'], content => { - return content.withMutations(map => { - data.forEach(value => { - const key = `${value.author}/${value.permlink}`; - if (!map.has(key)) { - value = fromJS(value) - value = value.set('stats', fromJS(contentStats(value))) - map.set(key, value); - } - }); + } + new_state = new_state.updateIn(['content'], content => { + return content.withMutations(map => { + data.forEach(value => { + const key = `${value.author}/${value.permlink}`; + value = fromJS(value) + value = value.set('stats', fromJS(contentStats(value))) + map.set(key, value); }); }); - // console.log('-- new_state -->', new_state.toJS()); - return new_state; - } - }, - { - action: 'REQUEST_META', // browser console debug - reducer: (state, {payload: {id, link}}) => - state.setIn(['metaLinkData', id], Map({link})) - }, - { - action: 'RECEIVE_META', // browser console debug - reducer: (state, {payload: {id, meta}}) => - state.updateIn(['metaLinkData', id], data => data.merge(meta)) - }, - { - action: 'SET', - reducer: (state, {payload: {key, value}}) => { - key = Array.isArray(key) ? key : [key] - return state.setIn(key, fromJS(value)) - } - }, - { - action: 'REMOVE', - reducer: (state, {payload: {key}}) => { - key = Array.isArray(key) ? key : [key] - return state.removeIn(key) - } - }, - { - action: 'UPDATE', - reducer: (state, {payload: {key, notSet = Map(), updater}}) => - // key = Array.isArray(key) ? key : [key] // TODO enable and test - state.updateIn(key, notSet, updater) - }, - { - action: 'SET_META_DATA', // browser console debug - reducer: (state, {payload: {id, meta}}) => - state.setIn(['metaLinkData', id], fromJS(meta)) - }, - { - action: 'CLEAR_META', // browser console debug - reducer: (state, {payload: {id}}) => - state.deleteIn(['metaLinkData', id]) - }, - { - action: 'CLEAR_META_ELEMENT', // browser console debug - reducer: (state, {payload: {formId, element}}) => - state.updateIn(['metaLinkData', formId], data => data.remove(element)) - }, - { - action: 'FETCH_JSON', - reducer: state => state // saga - }, - { - action: 'FETCH_JSON_RESULT', - reducer: (state, {payload: {id, result, error}}) => - state.set(id, fromJS({result, error})) - }, - { - action: 'SHOW_DIALOG', - reducer: (state, {payload: {name, params = {}}}) => - state.update('active_dialogs', Map(), d => d.set(name, fromJS({params}))) - }, - { - action: 'HIDE_DIALOG', - reducer: (state, {payload: {name}}) => - state.update('active_dialogs', d => d.delete(name)) - }, - - ] + }); + new_state = new_state.updateIn(['status', category || '', order], () => { + if (data.length < constants.FETCH_DATA_BATCH_SIZE) { + return {fetching: false, last_fetch: new Date()}; + } + return {fetching: false}; + }); + return new_state; + } + + case RECEIVE_RECENT_POSTS: { + const {data} = payload; + let new_state = state.updateIn(['discussion_idx', '', 'created'], list => { + if (!list) list = List(); + return list.withMutations(posts => { + data.forEach(value => { + const entry = `${value.author}/${value.permlink}`; + if (!posts.includes(entry)) posts.unshift(entry); + }); + }); + }); + new_state = new_state.updateIn(['content'], content => { + return content.withMutations(map => { + data.forEach(value => { + const key = `${value.author}/${value.permlink}`; + if (!map.has(key)) { + value = fromJS(value) + value = value.set('stats', fromJS(contentStats(value))) + map.set(key, value); + } + }); + }); + }); + return new_state; + } + + case REQUEST_META: { + const {id, link} = payload; + return state.setIn(['metaLinkData', id], Map({link})); + } + + case RECEIVE_META: { + const {id, meta} = payload; + return state.updateIn(['metaLinkData', id], data => data.merge(meta)); + } + + case SET: { + const {key, value} = payload; + const key_array = Array.isArray(key) ? key : [key]; + return state.setIn(key_array, fromJS(value)); + } + + case REMOVE: { + const key = Array.isArray(payload.key) ? payload.key : [payload.key]; + return state.removeIn(key); + } + + case UPDATE: { + const {key, notSet = Map(), updater} = payload; + return state.updateIn(key, notSet, updater); + } + + case SET_META_DATA: { + const {id, meta} = payload; + return state.setIn(['metaLinkData', id], fromJS(meta)); + } + + case CLEAR_META: { + return state.deleteIn(['metaLinkData', payload.id]); + } + + case CLEAR_META_ELEMENT: { + const {formId, element} = payload; + return state.updateIn(['metaLinkData', formId], data => data.remove(element)); + } + + case FETCH_JSON: { + return state; + } + + case FETCH_JSON_RESULT: { + const {id, result, error} = payload; + return state.set(id, fromJS({result, error})); + } + + case SHOW_DIALOG: { + const {name, params = {}} = payload; + return state.update('active_dialogs', Map(), d => d.set(name, fromJS({params}))); + } + + case HIDE_DIALOG: { + return state.update('active_dialogs', d => d.delete(payload.name)); + } + + default: + return state; + } +} + +// Action creators + +export const setCollapsed = (payload) => ({ + type: SET_COLLAPSED, + payload, +}); + +export const receiveState = (payload) => ({ + type: RECEIVE_STATE, + payload, +}); + +export const receiveAccount = (payload) => ({ + type: RECEIVE_ACCOUNT, + payload, +}); + +export const receiveComment = (payload) => ({ + type: RECEIVE_COMMENT, + payload, +}); + +export const receiveContent = (payload) => ({ + type: RECEIVE_CONTENT, + payload, +}); + +export const linkReply = (payload) => ({ + type: LINK_REPLY, + payload, +}); + +export const updateAccountWitnessVote = (payload) => ({ + type: UPDATE_ACCOUNT_WITNESS_VOTE, + payload, +}); + +export const updateAccountWitnessProxy = (payload) => ({ + type: UPDATE_ACCOUNT_WITNESS_PROXY, + payload, +}); + +export const deleteContent = (payload) => ({ + type: DELETE_CONTENT, + payload, +}); + +export const voted = (payload) => ({ + type: VOTED, + payload, +}); + +export const fetchingData = (payload) => ({ + type: FETCHING_DATA, + payload, +}); + +export const receiveData = (payload) => ({ + type: RECEIVE_DATA, + payload, +}); + +export const receiveRecentPosts = (payload) => ({ + type: RECEIVE_RECENT_POSTS, + payload, +}); + +export const requestMeta = (payload) => ({ + type: REQUEST_META, + payload, +}); + +export const receiveMeta = (payload) => ({ + type: RECEIVE_META, + payload, +}); + +export const set = (payload) => ({ + type: SET, + payload, +}); + +export const remove = (payload) => ({ + type: REMOVE, + payload, +}); + +export const update = (payload) => ({ + type: UPDATE, + payload, +}); + +export const setMetaData = (payload) => ({ + type: SET_META_DATA, + payload, +}); + +export const clearMeta = (payload) => ({ + type: CLEAR_META, + payload, +}); + +export const fetchJson = (payload) => ({ + type: FETCH_JSON, + payload, +}); + +export const fetchJsonResult = (payload) => ({ + type: FETCH_JSON_RESULT, + payload, +}); + +export const showDialog = (payload) => ({ + type: SHOW_DIALOG, + payload, +}); + +export const hideDialog = (payload) => ({ + type: HIDE_DIALOG, + payload, +}); + +export const getState = (payload) => ({ + type: GET_STATE, + payload, }); diff --git a/src/app/redux/MarketReducer.js b/src/app/redux/MarketReducer.js index 719acd810..bf0f45219 100644 --- a/src/app/redux/MarketReducer.js +++ b/src/app/redux/MarketReducer.js @@ -1,40 +1,67 @@ import {Map} from 'immutable'; -import createModule from 'redux-modules'; - - -export default createModule({ - name: 'market', - initialState: Map({status: {}}), - transformations: [ - { - action: 'RECEIVE_ORDERBOOK', - reducer: (state, action) => { - return state.set('orderbook', action.payload); - } - }, - { - action: 'RECEIVE_TICKER', - reducer: (state, action) => { - return state.set('ticker', action.payload); - } - }, - { - action: 'RECEIVE_OPEN_ORDERS', - reducer: (state, action) => { - return state.set('open_orders', action.payload); - } - }, - { - action: 'RECEIVE_TRADE_HISTORY', - reducer: (state, action) => { - return state.set('history', action.payload); - } - }, - { - action: 'APPEND_TRADE_HISTORY', - reducer: (state, action) => { - return state.set('history', [...action.payload, ...state.get('history')]); - } - } - ] + +// Action constants +const RECEIVE_ORDERBOOK = 'market/RECEIVE_ORDERBOOK'; +const RECEIVE_TICKER = 'market/RECEIVE_TICKER'; +const RECEIVE_OPEN_ORDERS = 'market/RECEIVE_OPEN_ORDERS'; +const RECEIVE_TRADE_HISTORY = 'market/RECEIVE_TRADE_HISTORY'; +const APPEND_TRADE_HISTORY = 'market/APPEND_TRADE_HISTORY'; +// Saga-related +export const UPDATE_MARKET = 'market/UPDATE_MARKET'; + +const defaultState = Map({status: {}}); + +export default function reducer(state = defaultState, action) { + const payload = action.payload; + + switch (action.type) { + case RECEIVE_ORDERBOOK: + return state.set('orderbook', payload); + + case RECEIVE_TICKER: + return state.set('ticker', payload); + + case RECEIVE_OPEN_ORDERS: + return state.set('open_orders', payload); + + case RECEIVE_TRADE_HISTORY: + return state.set('history', payload); + + case APPEND_TRADE_HISTORY: + return state.set('history', [...payload, ...state.get('history')]); + + default: + return state; + } +} + +// Action creators +export const receiveOrderbook = (payload) => ({ + type: RECEIVE_ORDERBOOK, + payload, +}); + +export const receiveTicker = (payload) => ({ + type: RECEIVE_TICKER, + payload, +}); + +export const receiveOpenOrders = (payload) => ({ + type: RECEIVE_OPEN_ORDERS, + payload, +}); + +export const receiveTradeHistory = (payload) => ({ + type: RECEIVE_TRADE_HISTORY, + payload, +}); + +export const appendTradeHistory = (payload) => ({ + type: APPEND_TRADE_HISTORY, + payload, +}); + +export const updateMarket = (payload) => ({ + type: UPDATE_MARKET, + payload, }); diff --git a/src/app/redux/MarketSaga.js b/src/app/redux/MarketSaga.js index a53329e7c..8c4016d88 100644 --- a/src/app/redux/MarketSaga.js +++ b/src/app/redux/MarketSaga.js @@ -1,9 +1,12 @@ import {takeLatest} from 'redux-saga'; import {call, put} from 'redux-saga/effects'; -import MarketReducer from './MarketReducer'; -import {getAccount} from './SagaShared'; import {api} from '@steemit/steem-js'; +import * as marketActions from './MarketReducer'; +import * as appActions from './AppReducer'; +import * as userActions from './UserReducer'; +import {getAccount} from './SagaShared'; + export const marketWatches = [watchLocationChange, watchUserLogin, watchMarketUpdate]; const wait = ms => ( @@ -29,27 +32,27 @@ export function* fetchMarket(location_change_action) { try { const state = yield call([api, api.getOrderBookAsync], 500); - yield put(MarketReducer.actions.receiveOrderbook(state)); + yield put(marketActions.receiveOrderbook(state)); let trades; if(last_trade == null ) { trades = yield call([api, api.getRecentTradesAsync], 25); - yield put(MarketReducer.actions.receiveTradeHistory(trades)); + yield put(marketActions.receiveTradeHistory(trades)); } else { let start = last_trade.toISOString().slice(0, -5) trades = yield call([api, api.getTradeHistoryAsync], start, "1969-12-31T23:59:59", 1000); trades = trades.reverse() - yield put(MarketReducer.actions.appendTradeHistory(trades)); + yield put(marketActions.appendTradeHistory(trades)); } if(trades.length > 0) { last_trade = new Date((new Date(Date.parse(trades[0]['date']))).getTime() + 1000) } const state3 = yield call([api, api.getTickerAsync]); - yield put(MarketReducer.actions.receiveTicker(state3)); + yield put(marketActions.receiveTicker(state3)); } catch (error) { console.error('~~ Saga fetchMarket error ~~>', error); - yield put({type: 'global/STEEM_API_ERROR', error: error.message}); + yield put(appActions.steemApiError(error.message)); } yield call(wait, 3000); @@ -61,11 +64,11 @@ export function* fetchOpenOrders(set_user_action) { try { const state = yield call([api, api.getOpenOrdersAsync], username); - yield put(MarketReducer.actions.receiveOpenOrders(state)); + yield put(marketActions.receiveOpenOrders(state)); yield call(getAccount, username, true); } catch (error) { console.error('~~ Saga fetchOpenOrders error ~~>', error); - yield put({type: 'global/STEEM_API_ERROR', error: error.message}); + yield put(appActions.steemApiError(error.message)); } } @@ -75,7 +78,7 @@ export function* reloadMarket(reload_action) { } export function* watchUserLogin() { - yield* takeLatest('user/SET_USER', fetchOpenOrders); + yield* takeLatest(userActions.SET_USER, fetchOpenOrders); } export function* watchLocationChange() { @@ -83,5 +86,5 @@ export function* watchLocationChange() { } export function* watchMarketUpdate() { - yield* takeLatest('market/UPDATE_MARKET', reloadMarket); + yield* takeLatest(marketActions.UPDATE_MARKET, reloadMarket); } diff --git a/src/app/redux/Offchain.jsx b/src/app/redux/OffchainReducer.js similarity index 100% rename from src/app/redux/Offchain.jsx rename to src/app/redux/OffchainReducer.js diff --git a/src/app/redux/PollDataSaga.js b/src/app/redux/PollDataSaga.js index d7946d13f..24295ac35 100644 --- a/src/app/redux/PollDataSaga.js +++ b/src/app/redux/PollDataSaga.js @@ -1,8 +1,9 @@ import { call, put, select } from 'redux-saga/effects'; -import GlobalReducer from './GlobalReducer'; +import {api} from '@steemit/steem-js'; + +import * as appActions from 'app/redux/AppReducer'; import {getNotifications, webPushRegister} from 'app/utils/ServerApiClient'; import registerServiceWorker from 'app/utils/RegisterServiceWorker'; -import {api} from '@steemit/steem-js'; const wait = ms => ( new Promise(resolve => { @@ -28,14 +29,11 @@ function* pollData() { } } const nc = yield call(getNotifications, username, webpush_params); - yield put({type: 'UPDATE_NOTIFICOUNTERS', payload: nc}); + yield put(appActions.updateNotificounters(nc)); } try { - const data = yield call([api, api.getDynamicGlobalPropertiesAsync]); - // console.log('-- pollData.pollData -->', data); - // const data = yield call([api, api.getDiscussionsByCreatedAsync], {limit: 10}); - // yield put(GlobalReducer.actions.receiveRecentPosts({data})); + yield call([api, api.getDynamicGlobalPropertiesAsync]); } catch (error) { console.error('~~ pollData saga error ~~>', error); } diff --git a/src/app/redux/RootReducer.js b/src/app/redux/RootReducer.js index 5743bdd55..7b65215a0 100644 --- a/src/app/redux/RootReducer.js +++ b/src/app/redux/RootReducer.js @@ -1,16 +1,15 @@ import {Map, fromJS} from 'immutable'; -import {combineReducers} from 'redux'; import {routerReducer} from 'react-router-redux'; -import appReducer from './AppReducer'; -//import discussionReducer from './DiscussionReducer'; -import globalReducerModule from './GlobalReducer'; -import marketReducerModule from './MarketReducer'; -import user from './User'; -// import auth from './AuthSaga'; -import transaction from './Transaction'; -import offchain from './Offchain'; +import {combineReducers} from 'redux'; import {reducer as formReducer} from 'redux-form'; // @deprecated, instead use: app/utils/ReactForm.js -import {contentStats} from 'app/utils/StateFunctions' + +import appReducer from './AppReducer'; +import globalReducer from './GlobalReducer'; +import marketReducer from './MarketReducer'; +import userReducer from './UserReducer'; +import transactionReducer from './TransactionReducer'; +import offchainReducer from './OffchainReducer'; +import {contentStats} from 'app/utils/StateFunctions'; function initReducer(reducer, type) { return (state, action) => { @@ -47,21 +46,13 @@ function initReducer(reducer, type) { } export default combineReducers({ - global: initReducer(globalReducerModule.reducer, 'global'), - market: initReducer(marketReducerModule.reducer), - offchain: initReducer(offchain), - user: initReducer(user.reducer), - // auth: initReducer(auth.reducer), - transaction: initReducer(transaction.reducer), - //discussion: initReducer(discussionReducer), + global: initReducer(globalReducer, 'global'), + market: initReducer(marketReducer), + offchain: initReducer(offchainReducer), + user: initReducer(userReducer), + transaction: initReducer(transactionReducer), discussion: initReducer((state = {}) => state), routing: initReducer(routerReducer), app: initReducer(appReducer), form: formReducer, }); - -/* -let now - benchStart: initReducer((state = {}, action) => {console.log('>> action.type', action.type); now = Date.now(); return state}), - benchEnd: initReducer((state = {}, action) => {console.log('<< action.type', action.type, (Date.now() - now), 'ms'); return state}), -*/ diff --git a/src/app/redux/SagaShared.js b/src/app/redux/SagaShared.js index 7a1de0cb8..04c4885a5 100644 --- a/src/app/redux/SagaShared.js +++ b/src/app/redux/SagaShared.js @@ -1,9 +1,12 @@ import {fromJS} from 'immutable' import {call, put, select} from 'redux-saga/effects'; -import g from 'app/redux/GlobalReducer' import {takeEvery, takeLatest} from 'redux-saga'; import tt from 'counterpart'; import {api} from '@steemit/steem-js'; + +import * as globalActions from './GlobalReducer' +import * as appActions from './AppReducer'; +import * as transactionActions from './TransactionReducer'; import {setUserPreferences} from 'app/utils/ServerApiClient'; const wait = ms => ( @@ -20,23 +23,23 @@ export function* getAccount(username, force = false) { [account] = yield call([api, api.getAccountsAsync], [username]) if(account) { account = fromJS(account) - yield put(g.actions.receiveAccount({account})) + yield put(globalActions.receiveAccount({account})) } } return account } export function* watchGetState() { - yield* takeEvery('global/GET_STATE', getState); + yield* takeEvery(globalActions.GET_STATE, getState); } /** Manual refreshes. The router is in FetchDataSaga. */ export function* getState({payload: {url}}) { try { const state = yield call([api, api.getStateAsync], url) - yield put(g.actions.receiveState(state)); + yield put(globalActions.receiveState(state)); } catch (error) { console.error('~~ Saga getState error ~~>', url, error); - yield put({type: 'global/STEEM_API_ERROR', error: error.message}); + yield put(appActions.steemApiError(error.message)); } } @@ -47,8 +50,8 @@ export function* watchTransactionErrors() { function* showTransactionErrorNotification() { const errors = yield select(state => state.transaction.get('errors')); for (const [key, message] of errors) { - yield put({type: 'ADD_NOTIFICATION', payload: {key, message}}); - yield put({type: 'transaction/DELETE_ERROR', payload: {key}}); + yield put(appActions.addNotification({key, message})); + yield put(transactionActions.deleteError({ key })); } } @@ -62,7 +65,7 @@ export function* getContent({author, permlink, resolve, reject}) { } } - yield put(g.actions.receiveContent({content})) + yield put(globalActions.receiveContent({content})) if (resolve && content) { resolve(content); } else if (reject && !content) { @@ -85,5 +88,5 @@ function* saveUserPreferences({payload}) { } function* watchUserSettingsUpdates() { - yield* takeLatest(['SET_USER_PREFERENCES', 'TOGGLE_NIGHTMODE', 'TOGGLE_BLOGMODE'], saveUserPreferences); + yield* takeLatest([appActions.SET_USER_PREFERENCES, appActions.TOGGLE_NIGHTMODE, appActions.TOGGLE_BLOGMODE], saveUserPreferences); } diff --git a/src/app/redux/Transaction.js b/src/app/redux/Transaction.js deleted file mode 100644 index 435e1a0b7..000000000 --- a/src/app/redux/Transaction.js +++ /dev/null @@ -1,140 +0,0 @@ -import {fromJS, Map} from 'immutable'; -import createModule from 'redux-modules'; - -export default createModule({ - name: 'transaction', - initialState: fromJS({ - operations: [], - status: { key: '', error: false, busy: false, }, - errors: null - }), - transformations: [ - { - action: 'CONFIRM_OPERATION', - reducer: (state, {payload}) => { - const operation = fromJS(payload.operation) - const confirm = payload.confirm - const warning = payload.warning - const checkbox = payload.checkbox - return state.merge({ - show_confirm_modal: true, - confirmBroadcastOperation: operation, - confirmErrorCallback: payload.errorCallback, - confirm, - warning, - checkbox - }) - } - }, - { action: 'HIDE_CONFIRM', reducer: state => - state.merge({show_confirm_modal: false, confirmBroadcastOperation: undefined, confirm: undefined}) - }, - { - // An error will end up in QUEUE - action: 'BROADCAST_OPERATION', - reducer: (state) => {//, {payload: {type, operation, keys}} - // See TransactionSaga.js - return state - }, - }, - { - // An error will end up in QUEUE - action: 'UPDATE_AUTHORITIES', - reducer: (state) => state, - }, - { - // An error will end up in QUEUE - action: 'UPDATE_META', - reducer: (state) => state, - }, - { - action: 'ERROR', - reducer: (state, {payload: {operations, error, errorCallback}}) => { - let errorStr = error.toString(); - let errorKey = 'Transaction broadcast error.'; - for (const [type/*, operation*/] of operations) { - switch (type) { - case 'vote': - if (/uniqueness constraint/.test(errorStr)) { - errorKey = 'You already voted for this post'; - console.error('You already voted for this post.') - } - break; - case 'comment': - if (/You may only post once per minute/.test(errorStr)) { - errorKey = 'You may only post once per minute.' - } else if (errorStr === 'Testing, fake error') - errorKey = 'Testing, fake error'; - break; - case 'transfer': - if (/get_balance/.test(errorStr)) { - errorKey = 'Insufficient balance.' - } - break; - case 'withdraw_vesting': - if(/Account registered by another account requires 10x account creation fee worth of Steem Power/.test(errorStr)) - errorKey = 'Account requires 10x the account creation fee in Steem Power (approximately 300 SP) before it can power down.' - break; - default: - break; - } - if (state.hasIn(['TransactionError', type + '_listener'])) { - state = state.setIn(['TransactionError', type], fromJS({key: errorKey, exception: errorStr})) - } else { - if (error.message) { - // Depends on FC_ASSERT formatting - // https://github.com/steemit/steemit.com/issues/222 - const err_lines = error.message.split('\n'); - if (err_lines.length > 2) { - errorKey = err_lines[1]; - const txt = errorKey.split(': '); - if(txt.length && txt[txt.length - 1].trim() !== '') { - errorKey = errorStr = txt[txt.length - 1] - } else - errorStr = `Transaction failed: ${err_lines[1]}`; - } - } - if (errorStr.length > 200) errorStr = errorStr.substring(0, 200); - // Catch for unknown key better error handling - if (/unknown key: /.test(errorKey)) { - errorKey = "Steem account doesn't exist."; - errorStr = "Transaction failed: Steem account doesn't exist."; - } - // Catch for invalid active authority - if (/Missing Active Authority /.test(errorKey)) { - errorKey = "Not your valid active key."; - errorStr = "Transaction failed: Not your valid active key."; - } - state = state.update('errors', errors => { - return errors ? errors.set(errorKey, errorStr) : Map({[errorKey]: errorStr}); - }); - } - } - if (errorCallback) try { errorCallback(errorKey) } catch (error2) { console.error(error2) } - return state - }, - }, - { - action: 'DELETE_ERROR', - reducer: (state, {payload: {key}}) => { - return state.deleteIn(['errors', key]); - } - }, - { - action: 'SET', - reducer: (state, {payload: {key, value}}) => { - key = Array.isArray(key) ? key : [key] - return state.setIn(key, fromJS(value)) - } - }, - { - action: 'REMOVE', - reducer: (state, {payload: {key}}) => { - key = Array.isArray(key) ? key : [key] - return state.removeIn(key) - } - }, - ] -}); - -// const log = v => {console.log('l', v); return v} diff --git a/src/app/redux/TransactionReducer.js b/src/app/redux/TransactionReducer.js new file mode 100644 index 000000000..0d1e70b20 --- /dev/null +++ b/src/app/redux/TransactionReducer.js @@ -0,0 +1,187 @@ +import {fromJS, Map} from 'immutable'; + +// Action constants +const CONFIRM_OPERATION = 'transaction/CONFIRM_OPERATION'; +const HIDE_CONFIRM = 'transaction/HIDE_CONFIRM'; +export const BROADCAST_OPERATION = 'transaction/BROADCAST_OPERATION'; +export const UPDATE_AUTHORITIES = 'transaction/UPDATE_AUTHORITIES'; +export const UPDATE_META = 'transaction/UPDATE_META'; +const ERROR = 'transaction/ERROR'; +const DELETE_ERROR = 'transaction/DELETE_ERROR'; +const SET = 'transaction/SET'; +const REMOVE = 'transaction/REMOVE'; +// Saga-related +export const RECOVER_ACCOUNT = 'transaction/RECOVER_ACCOUNT'; + +const defaultState = fromJS({ + operations: [], + status: { key: '', error: false, busy: false, }, + errors: null +}); + +export default function reducer(state = defaultState, action) { + const payload = action.payload; + + switch (action.type) { + case CONFIRM_OPERATION: { + const operation = fromJS(payload.operation); + const confirm = payload.confirm; + const warning = payload.warning; + return state.merge({ + show_confirm_modal: true, + confirmBroadcastOperation: operation, + confirmErrorCallback: payload.errorCallback, + confirm, + warning + }); + } + + case HIDE_CONFIRM: + return state.merge({show_confirm_modal: false, confirmBroadcastOperation: undefined, confirm: undefined}); + + case BROADCAST_OPERATION: + // See TransactionSaga.js + return state; + + case UPDATE_AUTHORITIES: + return state; + + case UPDATE_META: + return state; + + case ERROR: { + const {operations, error, errorCallback} = payload; + let errorStr = error.toString(); + let errorKey = 'Transaction broadcast error.'; + for (const [type/*, operation*/] of operations) { + switch (type) { + case 'vote': + if (/uniqueness constraint/.test(errorStr)) { + errorKey = 'You already voted for this post'; + console.error('You already voted for this post.') + } + break; + case 'comment': + if (/You may only post once per minute/.test(errorStr)) { + errorKey = 'You may only post once per minute.' + } else if (errorStr === 'Testing, fake error') + errorKey = 'Testing, fake error'; + break; + case 'transfer': + if (/get_balance/.test(errorStr)) { + errorKey = 'Insufficient balance.' + } + break; + case 'withdraw_vesting': + if(/Account registered by another account requires 10x account creation fee worth of Steem Power/.test(errorStr)) + errorKey = 'Account requires 10x the account creation fee in Steem Power (approximately 300 SP) before it can power down.' + break; + default: + break; + } + if (state.hasIn(['TransactionError', type + '_listener'])) { + state = state.setIn(['TransactionError', type], fromJS({key: errorKey, exception: errorStr})) + } else { + if (error.message) { + // Depends on FC_ASSERT formatting + // https://github.com/steemit/steemit.com/issues/222 + const err_lines = error.message.split('\n'); + if (err_lines.length > 2) { + errorKey = err_lines[1]; + const txt = errorKey.split(': '); + if(txt.length && txt[txt.length - 1].trim() !== '') { + errorKey = errorStr = txt[txt.length - 1]; + } else + errorStr = `Transaction failed: ${err_lines[1]}`; + } + } + if (errorStr.length > 200) errorStr = errorStr.substring(0, 200); + // Catch for unknown key better error handling + if (/unknown key: /.test(errorKey)) { + errorKey = "Steem account doesn't exist."; + errorStr = "Transaction failed: Steem account doesn't exist."; + } + // Catch for invalid active authority + if (/Missing Active Authority /.test(errorKey)) { + errorKey = "Not your valid active key."; + errorStr = "Transaction failed: Not your valid active key."; + } + state = state.update('errors', (errors) => { + return errors ? errors.set(errorKey, errorStr) : Map({[errorKey]: errorStr}); + }); + } + } + + if (errorCallback) { + errorCallback(errorKey); + } else { + throw new Error('PANIC: no callback registered to handle error ' + errorKey); + } + + return state; + } + + case DELETE_ERROR: + return state.deleteIn(['errors', payload.key]); + + case SET: + return state.setIn(Array.isArray(payload.key) ? payload.key : [payload.key], fromJS(payload.value)) + + case REMOVE: + return state.removeIn(Array.isArray(payload.key) ? payload.key : [payload.key]); + + default: + return state; + } +} + +// Action creators +export const confirmOperation = (payload) => ({ + type: CONFIRM_OPERATION, + payload, +}); + +export const hideConfirm = (payload) => ({ + type: HIDE_CONFIRM, + payload, +}); + +export const broadcastOperation = (payload) => ({ + type: BROADCAST_OPERATION, + payload, +}); + +export const updateAuthorities = (payload) => ({ + type: UPDATE_AUTHORITIES, + payload, +}); + +export const updateMeta = (payload) => ({ + type: UPDATE_META, + payload, +}); + +export const error = (payload) => ({ + type: ERROR, + payload, +}); + +export const deleteError = (payload) => ({ + type: DELETE_ERROR, + payload, +}); + +export const set = (payload) => ({ + type: SET, + payload, +}); + +export const remove = (payload) => ({ + type: REMOVE, + payload, +}); + +export const recoverAccount = (payload) => ({ + type: RECOVER_ACCOUNT, + payload, +}); diff --git a/src/app/redux/TransactionSaga.js b/src/app/redux/TransactionSaga.js index 34c0876c4..ed468b5f4 100644 --- a/src/app/redux/TransactionSaga.js +++ b/src/app/redux/TransactionSaga.js @@ -1,18 +1,22 @@ import {takeEvery} from 'redux-saga'; import {call, put, select} from 'redux-saga/effects'; import {fromJS, Set, Map} from 'immutable' -import {getAccount, getContent} from 'app/redux/SagaShared' -import {findSigningKey} from 'app/redux/AuthSaga' -import g from 'app/redux/GlobalReducer' -import user from 'app/redux/User' -import tr from 'app/redux/Transaction' -import tt from 'counterpart' -import getSlug from 'speakingurl' -import {DEBT_TICKER} from 'app/client_config' -import {serverApiRecordEvent} from 'app/utils/ServerApiClient' +import tt from 'counterpart'; +import getSlug from 'speakingurl'; +import base58 from 'bs58'; +import secureRandom from 'secure-random'; import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc'; import {api, broadcast, auth, memo} from '@steemit/steem-js'; +import {getAccount, getContent} from 'app/redux/SagaShared'; +import {findSigningKey} from 'app/redux/AuthSaga' +import * as appActions from 'app/redux/AppReducer'; +import * as globalActions from 'app/redux/GlobalReducer'; +import * as transactionActions from 'app/redux/TransactionReducer'; +import * as userActions from 'app/redux/UserReducer'; +import {DEBT_TICKER} from 'app/client_config'; +import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; + export const transactionWatches = [ watchForBroadcast, watchForUpdateAuthorities, @@ -21,16 +25,16 @@ export const transactionWatches = [ ] export function* watchForBroadcast() { - yield* takeEvery('transaction/BROADCAST_OPERATION', broadcastOperation); + yield* takeEvery(transactionActions.BROADCAST_OPERATION, broadcastOperation); } export function* watchForUpdateAuthorities() { - yield* takeEvery('transaction/UPDATE_AUTHORITIES', updateAuthorities); + yield* takeEvery(transactionActions.UPDATE_AUTHORITIES, updateAuthorities); } export function* watchForUpdateMeta() { - yield* takeEvery('transaction/UPDATE_META', updateMeta); + yield* takeEvery(transactionActions.UPDATE_META, updateMeta); } export function* watchForRecoverAccount() { - yield* takeEvery('transaction/RECOVER_ACCOUNT', recoverAccount); + yield* takeEvery(transactionActions.RECOVER_ACCOUNT, recoverAccount); } const hook = { @@ -75,14 +79,14 @@ function* preBroadcast_vote({operation, username}) { if (!operation.voter) operation.voter = username const {voter, author, permlink, weight} = operation // give immediate feedback - yield put(g.actions.set({key: `transaction_vote_active_${author}_${permlink}`, value: true})) - yield put(g.actions.voted({username: voter, author, permlink, weight})) + yield put(globalActions.set({key: `transaction_vote_active_${author}_${permlink}`, value: true})); + yield put(globalActions.voted({username: voter, author, permlink, weight})); return operation } function* preBroadcast_account_witness_vote({operation, username}) { if (!operation.account) operation.account = username const {account, witness, approve} = operation - yield put(g.actions.updateAccountWitnessVote({account, witness, approve})) + yield put(globalActions.updateAccountWitnessVote({account, witness, approve})); return operation } @@ -92,7 +96,7 @@ function* preBroadcast_custom_json({operation}) { try { if(json[0] === 'follow') { const {follower, following, what: [action]} = json[1] - yield put(g.actions.update({ + yield put(globalActions.update({ key: ['follow', 'getFollowingAsync', follower], notSet: Map(), updater: m => { @@ -111,7 +115,7 @@ function* preBroadcast_custom_json({operation}) { m = m.set('ignore_count', m.get('ignore_result', Set()).size) return m//.asImmutable() } - })) + })); } } catch(e) { console.error('TransactionSaga unrecognized follow custom_json format', operation.json); @@ -121,7 +125,7 @@ function* preBroadcast_custom_json({operation}) { } function* error_account_witness_vote({operation: {account, witness, approve}}) { - yield put(g.actions.updateAccountWitnessVote({account, witness, approve: !approve})) + yield put(globalActions.updateAccountWitnessVote({account, witness, approve: !approve})); } /** Keys, username, and password are not needed for the initial call. This will check the login and may trigger an action to prompt for the password / key. */ @@ -132,7 +136,7 @@ function* broadcastOperation({payload: const conf = typeof confirm === 'function' ? confirm() : confirm if(conf) { - yield put(tr.actions.confirmOperation({confirm, warning, operation: operationParam, errorCallback})) + yield put(transactionActions.confirmOperation({ confirm, warning, operation: operationParam, errorCallback })); return } const payload = {operations: [[type, operation]], keys, username, successCallback, errorCallback} @@ -141,7 +145,7 @@ function* broadcastOperation({payload: const warning = tt('g.post_key_warning.warning') const checkbox = tt('g.post_key_warning.checkbox') operationParam.allowPostUnsafe = true - yield put(tr.actions.confirmOperation({confirm, warning, checkbox, operation: operationParam, errorCallback})) + yield put(transactionActions.confirmOperation({ confirm, warning, checkbox, operation: operationParam, errorCallback })); return } try { @@ -153,7 +157,7 @@ function* broadcastOperation({payload: payload.keys.push(signingKey) else { if (!password) { - yield put(user.actions.showLogin({operation: {type, operation, username, successCallback, errorCallback, saveLogin: true}})) + yield put(userActions.showLogin({ operation: { type, operation, username, successCallback, errorCallback, saveLogin: true } })); return } } @@ -190,7 +194,7 @@ function* broadcastPayload({payload: {operations, keys, username, successCallbac // console.log('broadcastPayload') if ($STM_Config.read_only_mode) return; for (const [type] of operations) // see also transaction/ERROR - yield put(tr.actions.remove({key: ['TransactionError', type]})) + yield put(transactionActions.remove({ key: ['TransactionError', type] })); { const newOps = [] @@ -257,18 +261,18 @@ function* broadcastPayload({payload: {operations, keys, username, successCallbac } const config = operation.__config if (config && config.successMessage) { - yield put({type: 'ADD_NOTIFICATION', payload: { + yield put(appActions.addNotification({ key: "trx_" + Date.now(), message: config.successMessage, - dismissAfter: 5000 - }}) + dismissAfter: 5000, + })); } } if (successCallback) try { successCallback() } catch (error) { console.error(error) } } catch (error) { console.error('TransactionSaga\tbroadcastPayload', error); // status: error - yield put(tr.actions.error({operations, error, errorCallback})); + yield put(transactionActions.error({ operations, error, errorCallback })); for (const [type, operation] of operations) { if (hook['error_' + type]) { try { @@ -286,31 +290,31 @@ function* accepted_comment({operation}) { // update again with new $$ amount from the steemd node yield call(getContent, {author, permlink}) // receiveComment did the linking already (but that is commented out) - yield put(g.actions.linkReply(operation)) + yield put(globalActions.linkReply(operation)); // mark the time (can only post 1 per min) // yield put(user.actions.acceptedComment()) } function* accepted_delete_comment({operation}) { - yield put(g.actions.deleteContent(operation)) + yield put(globalActions.deleteContent(operation)); } function* accepted_vote({operation: {author, permlink, weight}}) { console.log('Vote accepted, weight', weight, 'on', author + '/' + permlink, 'weight'); // update again with new $$ amount from the steemd node - yield put(g.actions.remove({key: `transaction_vote_active_${author}_${permlink}`})) + yield put(globalActions.remove({ key: `transaction_vote_active_${author}_${permlink}` })); yield call(getContent, {author, permlink}) } function* accepted_withdraw_vesting({operation}) { let [account] = yield call([api, api.getAccountsAsync], [operation.account]) account = fromJS(account) - yield put(g.actions.receiveAccount({account})) + yield put(globalActions.receiveAccount({ account })); } function* accepted_account_update({operation}) { let [account] = yield call([api, api.getAccountsAsync], [operation.account]) account = fromJS(account) - yield put(g.actions.receiveAccount({account})) + yield put(globalActions.receiveAccount({ account })); // bug, fork, etc.. the folowing would be mis-leading // const {account} = operation @@ -339,9 +343,6 @@ function* accepted_account_update({operation}) { // } // } -import base58 from 'bs58' -import secureRandom from 'secure-random' - // function* preBroadcast_account_witness_vote({operation, username}) { // } function* preBroadcast_comment({operation, username}) { @@ -454,14 +455,14 @@ function createPatch(text1, text2) { function* error_custom_json({operation: {id, required_posting_auths}}) { if(id === 'follow') { const follower = required_posting_auths[0] - yield put(g.actions.update({ + yield put(globalActions.update({ key: ['follow', 'getFollowingAsync', follower, 'loading'], updater: () => null - })) + })); } } function* error_vote({operation: {author, permlink}}) { - yield put(g.actions.remove({key: `transaction_vote_active_${author}_${permlink}`})); + yield put(globalActions.remove({ key: `transaction_vote_active_${author}_${permlink}` })); yield call(getContent, {author, permlink}); // unvote } diff --git a/src/app/redux/User.js b/src/app/redux/User.js deleted file mode 100644 index 6e84ee16f..000000000 --- a/src/app/redux/User.js +++ /dev/null @@ -1,155 +0,0 @@ -import {fromJS} from 'immutable'; -import createModule from 'redux-modules'; -import { DEFAULT_LANGUAGE } from 'app/client_config'; -import store from 'store'; - -const defaultState = fromJS({ - current: null, - show_login_modal: false, - show_transfer_modal: false, - show_powerdown_modal: false, - show_promote_post_modal: false, - show_signup_modal: false, - pub_keys_used: null, - locale: DEFAULT_LANGUAGE, -}); - -if (process.env.BROWSER) { - const locale = store.get('language'); - if (locale) defaultState.locale = locale; -} - -export default createModule({ - name: 'user', - initialState: defaultState, - transformations: [ - { - action: 'SHOW_LOGIN', - reducer: (state, {payload}) => { - // https://github.com/mboperator/redux-modules/issues/11 - if (typeof payload === 'function') payload = undefined; - let operation, loginDefault - if(payload) { - operation = fromJS(payload.operation) - loginDefault = fromJS(payload.loginDefault) - } - return state.merge({show_login_modal: true, loginBroadcastOperation: operation, loginDefault}) - } - }, - { - action: 'SHOW_TERMS', - reducer: (state, {payload}) => { - // https://github.com/mboperator/redux-modules/issues/11 - if (typeof payload === 'function') payload = undefined; - let operation, termsDefault; - if(payload) { - operation = fromJS(payload.operation); - termsDefault = fromJS(payload.termsDefault) - } - return state.merge({show_terms_modal: true, loginBroadcastOperation: operation, termsDefault}) - } - }, - { action: 'HIDE_LOGIN', reducer: state => - state.merge({show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined}) }, - { action: 'SAVE_LOGIN_CONFIRM', reducer: (state, {payload}) => state.set('saveLoginConfirm', payload) }, - { action: 'SAVE_LOGIN', reducer: (state) => state }, // Use only for low security keys (like posting only keys) - { action: 'REMOVE_HIGH_SECURITY_KEYS', reducer: (state) => { - if(!state.hasIn(['current', 'private_keys'])) return state - let empty = false - state = state.updateIn(['current', 'private_keys'], private_keys => { - if(!private_keys) return null - if(private_keys.has('active_private')) - console.log('removeHighSecurityKeys') - private_keys = private_keys.delete('active_private') - empty = private_keys.size === 0 - return private_keys - }) - if(empty) { - // User logged in with Active key then navigates away from the page - // LOGOUT - return defaultState.merge({logged_out: true}) - } - const username = state.getIn(['current', 'username']) - state = state.setIn(['authority', username, 'active'], 'none') - state = state.setIn(['authority', username, 'owner'], 'none') - return state - }}, - { action: 'CHANGE_LANGUAGE', reducer: (state, {payload}) => { - return state.set('locale', payload)} - }, - { action: 'SHOW_TRANSFER', reducer: state => state.set('show_transfer_modal', true) }, - { action: 'HIDE_TRANSFER', reducer: state => state.set('show_transfer_modal', false) }, - { action: 'SHOW_POWERDOWN', reducer: state => state.set('show_powerdown_modal', true) }, - { action: 'HIDE_POWERDOWN', reducer: state => state.set('show_powerdown_modal', false) }, - { action: 'SHOW_PROMOTE_POST', reducer: state => state.set('show_promote_post_modal', true) }, - { action: 'HIDE_PROMOTE_POST', reducer: state => state.set('show_promote_post_modal', false) }, - { action: 'SET_TRANSFER_DEFAULTS', reducer: (state, {payload}) => state.set('transfer_defaults', fromJS(payload)) }, - { action: 'CLEAR_TRANSFER_DEFAULTS', reducer: (state) => state.remove('transfer_defaults') }, - { action: 'SET_POWERDOWN_DEFAULTS', reducer: (state, {payload}) => state.set('powerdown_defaults', fromJS(payload)) }, - { action: 'CLEAR_POWERDOWN_DEFAULTS', reducer: (state) => state.remove('powerdown_defaults') }, - { - action: 'USERNAME_PASSWORD_LOGIN', - reducer: state => state, // saga - }, - { - action: 'SET_USER', - reducer: (state, {payload}) => { - // console.log('SET_USER') - if (payload.vesting_shares) payload.vesting_shares = parseFloat(payload.vesting_shares); - if (payload.delegated_vesting_shares) payload.delegated_vesting_shares = parseFloat(payload.delegated_vesting_shares); - if (payload.received_vesting_shares) payload.received_vesting_shares = parseFloat(payload.received_vesting_shares); - return state.mergeDeep({ current: payload, show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined, logged_out: undefined }) - } - }, - { - action: 'CLOSE_LOGIN', - reducer: (state) => state.merge({ login_error: undefined, show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined }) - }, - { - action: 'LOGIN_ERROR', - reducer: (state, {payload: {error}}) => state.merge({ login_error: error, logged_out: undefined }) - }, - { - action: 'LOGOUT', - reducer: () => { - return defaultState.merge({logged_out: true}) - } - }, - // { - // action: 'ACCEPTED_COMMENT', - // // User can only post 1 comment per minute - // reducer: (state) => state.merge({ current: {lastComment: Date.now()} }) - // }, - { action: 'SHOW_SIGN_UP', reducer: state => state.set('show_signup_modal', true) }, - { action: 'HIDE_SIGN_UP', reducer: state => state.set('show_signup_modal', false) }, - - { - action: 'KEYS_ERROR', - reducer: (state, {payload: {error}}) => state.merge({ keys_error: error }) - }, - // { action: 'UPDATE_PERMISSIONS', reducer: state => { - // return state // saga - // }}, - { // AuthSaga - action: 'ACCOUNT_AUTH_LOOKUP', - reducer: state => state - }, - { // AuthSaga - action: 'SET_AUTHORITY', - reducer: (state, {payload: {accountName, auth, pub_keys_used}}) => { - state = state.setIn(['authority', accountName], fromJS(auth)) - if(pub_keys_used) - state = state.set('pub_keys_used', pub_keys_used) - return state - }, - }, - { action: 'HIDE_CONNECTION_ERROR_MODAL', reducer: state => state.set('hide_connection_error_modal', true) }, - { - action: 'SET', - reducer: (state, {payload: {key, value}}) => { - key = Array.isArray(key) ? key : [key] - return state.setIn(key, fromJS(value)) - } - }, - ] -}); diff --git a/src/app/redux/UserActions.js b/src/app/redux/UserActions.js deleted file mode 100644 index bfae60e68..000000000 --- a/src/app/redux/UserActions.js +++ /dev/null @@ -1,6 +0,0 @@ -// export function setUser(user) { -// return { -// type: 'SET_USER', -// username: user ? user.name : null -// }; -// } diff --git a/src/app/redux/UserReducer.js b/src/app/redux/UserReducer.js new file mode 100644 index 000000000..d0a9f46a8 --- /dev/null +++ b/src/app/redux/UserReducer.js @@ -0,0 +1,345 @@ +import {fromJS} from 'immutable'; +import { DEFAULT_LANGUAGE } from 'app/client_config'; +import store from 'store'; + +// Action constants +const SHOW_LOGIN = 'user/SHOW_LOGIN'; +const SHOW_TERMS = 'user/SHOW_TERMS'; +const HIDE_LOGIN = 'user/HIDE_LOGIN'; +export const SAVE_LOGIN_CONFIRM = 'user/SAVE_LOGIN_CONFIRM'; +export const SAVE_LOGIN = 'user/SAVE_LOGIN'; +const REMOVE_HIGH_SECURITY_KEYS = 'user/REMOVE_HIGH_SECURITY_KEYS'; +const CHANGE_LANGUAGE = 'user/CHANGE_LANGUAGE'; +const SHOW_TRANSFER = 'user/SHOW_TRANSFER'; +const HIDE_TRANSFER = 'user/HIDE_TRANSFER'; +const SHOW_POWERDOWN = 'user/SHOW_POWERDOWN'; +const HIDE_POWERDOWN = 'user/HIDE_POWERDOWN'; +const SHOW_PROMOTE_POST = 'user/SHOW_PROMOTE_POST'; +const HIDE_PROMOTE_POST = 'user/HIDE_PROMOTE_POST'; +const SET_TRANSFER_DEFAULTS = 'user/SET_TRANSFER_DEFAULTS'; +const CLEAR_TRANSFER_DEFAULTS = 'user/CLEAR_TRANSFER_DEFAULTS'; +const SET_POWERDOWN_DEFAULTS = 'user/SET_POWERDOWN_DEFAULTS'; +const CLEAR_POWERDOWN_DEFAULTS = 'user/CLEAR_POWERDOWN_DEFAULTS'; +export const USERNAME_PASSWORD_LOGIN = 'user/USERNAME_PASSWORD_LOGIN'; +export const SET_USER = 'user/SET_USER'; +const CLOSE_LOGIN = 'user/CLOSE_LOGIN'; +export const LOGIN_ERROR = 'user/LOGIN_ERROR'; +export const LOGOUT = 'user/LOGOUT'; +const SHOW_SIGN_UP = 'user/SHOW_SIGN_UP'; +const HIDE_SIGN_UP = 'user/HIDE_SIGN_UP'; +const KEYS_ERROR = 'user/KEYS_ERROR'; +const ACCOUNT_AUTH_LOOKUP = 'user/ACCOUNT_AUTH_LOOKUP'; +const SET_AUTHORITY = 'user/SET_AUTHORITY'; +const HIDE_CONNECTION_ERROR_MODAL = 'user/HIDE_CONNECTION_ERROR_MODAL'; +const SET = 'user/SET'; +// Saga-related +export const LOAD_SAVINGS_WITHDRAW = 'user/LOAD_SAVINGS_WITHDRAW'; +export const UPLOAD_IMAGE = 'user/UPLOAD_IMAGE'; + +const defaultState = fromJS({ + current: null, + show_login_modal: false, + show_transfer_modal: false, + show_promote_post_modal: false, + show_signup_modal: false, + pub_keys_used: null, + locale: DEFAULT_LANGUAGE +}); + +if (process.env.BROWSER) { + const locale = store.get('language'); + if (locale) defaultState.locale = locale; +} + +export default function reducer(state = defaultState, action) { + const payload = action.payload; + + switch (action.type) { + case SHOW_LOGIN: { + let operation, loginDefault; + if (payload) { + operation = fromJS(payload.operation); + loginDefault = fromJS(payload.loginDefault); + } + return state.merge({show_login_modal: true, loginBroadcastOperation: operation, loginDefault}); + } + + case SHOW_TERMS: { + let operation, termsDefault; + if (payload) { + operation = fromJS(payload.operation); + termsDefault = fromJS(payload.termsDefault); + } + return state.merge({show_terms_modal: true, loginBroadcastOperation: operation, termsDefault}); + } + + case HIDE_LOGIN: + return state.merge({show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined}); + + case SAVE_LOGIN_CONFIRM: + return state.set('saveLoginConfirm', payload); + + case SAVE_LOGIN: + // Use only for low security keys (like posting only keys) + return state; + + case REMOVE_HIGH_SECURITY_KEYS: { + if (!state.hasIn(['current', 'private_keys'])) return state; + let empty = false; + state = state.updateIn(['current', 'private_keys'], private_keys => { + if (!private_keys) return null; + if (private_keys.has('active_private')) console.log('removeHighSecurityKeys'); + private_keys = private_keys.delete('active_private'); + empty = private_keys.size === 0; + return private_keys; + }) + if (empty) { + // User logged in with Active key then navigates away from the page + // LOGOUT + return defaultState.merge({logged_out: true}); + } + const username = state.getIn(['current', 'username']); + state = state.setIn(['authority', username, 'active'], 'none'); + state = state.setIn(['authority', username, 'owner'], 'none'); + return state; + } + + case CHANGE_LANGUAGE: + return state.set('locale', payload); + + case SHOW_TRANSFER: + return state.set('show_transfer_modal', true); + + case HIDE_TRANSFER: + return state.set('show_transfer_modal', false); + + case SHOW_POWERDOWN: + return state.set('show_powerdown_modal', true); + + case HIDE_POWERDOWN: + return state.set('show_powerdown_modal', false); + + case SHOW_PROMOTE_POST: + return state.set('show_promote_post_modal', true); + + case HIDE_PROMOTE_POST: + return state.set('show_promote_post_modal', false); + + case SET_TRANSFER_DEFAULTS: + return state.set('transfer_defaults', fromJS(payload)); + + case CLEAR_TRANSFER_DEFAULTS: + return state.remove('transfer_defaults'); + + case SET_POWERDOWN_DEFAULTS: + return state.set('powerdown_defaults', fromJS(payload)); + + case CLEAR_POWERDOWN_DEFAULTS: + return state.remove('powerdown_defaults'); + + case USERNAME_PASSWORD_LOGIN: + case LOAD_SAVINGS_WITHDRAW: + return state; // saga + + case SET_USER: + if (payload.vesting_shares) payload.vesting_shares = parseFloat(payload.vesting_shares); + if (payload.delegated_vesting_shares) payload.delegated_vesting_shares = parseFloat(payload.delegated_vesting_shares); + if (payload.received_vesting_shares) payload.received_vesting_shares = parseFloat(payload.received_vesting_shares); + return state.mergeDeep({ current: payload, show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined, logged_out: undefined }); + + case CLOSE_LOGIN: + return state.merge({ login_error: undefined, show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined }); + + case LOGIN_ERROR: + return state.merge({ login_error: payload.error, logged_out: undefined }); + + case LOGOUT: + return defaultState.merge({logged_out: true}); + + case SHOW_SIGN_UP: + return state.set('show_signup_modal', true); + + case HIDE_SIGN_UP: + return state.set('show_signup_modal', false); + + case KEYS_ERROR: + return state.merge({ keys_error: payload.error }) + + case ACCOUNT_AUTH_LOOKUP: + // AuthSaga + return state; + + case SET_AUTHORITY: { + // AuthSaga + const {accountName, auth, pub_keys_used} = payload; + state = state.setIn(['authority', accountName], fromJS(auth)); + if (pub_keys_used) state = state.set('pub_keys_used', pub_keys_used); + return state; + } + + case HIDE_CONNECTION_ERROR_MODAL: + return state.set('hide_connection_error_modal', true); + + case SET: + return state.setIn(Array.isArray(payload.key) ? payload.key : [payload.key], fromJS(payload.value)); + + default: + return state; + } +} + +// Action creators +export const showLogin = (payload) => ({ + type: SHOW_LOGIN, + payload, +}); + +export const showTerms = (payload) => ({ + type: SHOW_TERMS, + payload, +}); + +export const hideLogin = (payload) => ({ + type: HIDE_LOGIN, + payload, +}); + +export const saveLoginConfirm = (payload) => ({ + type: SAVE_LOGIN_CONFIRM, + payload, +}); + +export const saveLogin = (payload) => ({ + type: SAVE_LOGIN, + payload, +}); + +export const removeHighSecurityKeys = (payload) => ({ + type: REMOVE_HIGH_SECURITY_KEYS, + payload, +}); + +export const changeLanguage = (payload) => ({ + type: CHANGE_LANGUAGE, + payload, +}); + +export const showTransfer = (payload) => ({ + type: SHOW_TRANSFER, + payload, +}); + +export const hideTransfer = (payload) => ({ + type: HIDE_TRANSFER, + payload, +}); + +export const showPowerdown = (payload) => ({ + type: SHOW_POWERDOWN, + payload, +}); + +export const hidePowerdown = (payload) => ({ + type: HIDE_POWERDOWN, + payload, +}); + +export const showPromotePost = (payload) => ({ + type: SHOW_PROMOTE_POST, + payload, +}); + +export const hidePromotePost = (payload) => ({ + type: HIDE_PROMOTE_POST, + payload, +}); + +export const setTransferDefaults = (payload) => ({ + type: SET_TRANSFER_DEFAULTS, + payload, +}); + +export const clearTransferDefaults = (payload) => ({ + type: CLEAR_TRANSFER_DEFAULTS, + payload, +}); + +export const setPowerdownDefaults = (payload) => ({ + type: SET_POWERDOWN_DEFAULTS, + payload, +}); + +export const clearPowerdownDefaults = (payload) => ({ + type: CLEAR_POWERDOWN_DEFAULTS, + payload, +}); + +export const usernamePasswordLogin = (payload) => ({ + type: USERNAME_PASSWORD_LOGIN, + payload, +}); + +export const setUser = (payload) => ({ + type: SET_USER, + payload, +}); + +export const closeLogin = (payload) => ({ + type: CLOSE_LOGIN, + payload, +}); + +export const loginError = (payload) => ({ + type: LOGIN_ERROR, + payload, +}); + +export const logout = (payload) => ({ + type: LOGOUT, + payload, +}); + +export const showSignUp = (payload) => ({ + type: SHOW_SIGN_UP, + payload, +}); + +export const hideSignUp = (payload) => ({ + type: HIDE_SIGN_UP, + payload, +}); + +export const keysError = (payload) => ({ + type: KEYS_ERROR, + payload, +}); + +export const accountAuthLookup = (payload) => ({ + type: ACCOUNT_AUTH_LOOKUP, + payload, +}); + +export const setAuthority = (payload) => ({ + type: SET_AUTHORITY, + payload, +}); + +export const hideConnectionErrorModal = (payload) => ({ + type: HIDE_CONNECTION_ERROR_MODAL, + payload, +}); + +export const set = (payload) => ({ + type: SET, + payload, +}); + +export const loadSavingsWithdraw = (payload) => ({ + type: LOAD_SAVINGS_WITHDRAW, + payload, +}); + +export const uploadImage = (payload) => ({ + type: UPLOAD_IMAGE, + payload, +}); diff --git a/src/app/redux/UserSaga.js b/src/app/redux/UserSaga.js index ad7aae4b9..412a9150c 100644 --- a/src/app/redux/UserSaga.js +++ b/src/app/redux/UserSaga.js @@ -1,15 +1,19 @@ import {fromJS, Set, List} from 'immutable' import {takeLatest} from 'redux-saga'; import {call, put, select, fork} from 'redux-saga/effects'; +import {api} from '@steemit/steem-js'; +import {PrivateKey, Signature, hash} from '@steemit/steem-js/lib/auth/ecc'; + import {accountAuthLookup} from 'app/redux/AuthSaga' -import user from 'app/redux/User' import {getAccount} from 'app/redux/SagaShared' +import * as userActions from 'app/redux/UserReducer'; import {browserHistory} from 'react-router' -import {serverApiLogin, serverApiLogout} from 'app/utils/ServerApiClient'; -import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; +import { + serverApiLogin, + serverApiLogout, + serverApiRecordEvent, +} from 'app/utils/ServerApiClient'; import {loadFollows} from 'app/redux/FollowSaga' -import {PrivateKey, Signature, hash} from '@steemit/steem-js/lib/auth/ecc'; -import {api} from '@steemit/steem-js'; import {translate} from 'app/Translator'; import DMCAUserList from 'app/utils/DMCAUserList'; @@ -26,27 +30,27 @@ export const userWatches = [ uploadImageWatch, ] -const highSecurityPages = Array(/\/market/, /\/@.+\/(transfers|permissions|password)/, /\/~witnesses/) +const highSecurityPages = [/\/market/, /\/@.+\/(transfers|permissions|password)/, /\/~witnesses/] function* lookupPreviousOwnerAuthorityWatch() { yield* takeLatest('user/lookupPreviousOwnerAuthority', lookupPreviousOwnerAuthority); } function* loginWatch() { - yield* takeLatest('user/USERNAME_PASSWORD_LOGIN', usernamePasswordLogin); + yield* takeLatest(userActions.USERNAME_PASSWORD_LOGIN, usernamePasswordLogin); } function* saveLoginWatch() { - yield* takeLatest('user/SAVE_LOGIN', saveLogin_localStorage); + yield* takeLatest(userActions.SAVE_LOGIN, saveLogin_localStorage); } function* logoutWatch() { - yield* takeLatest('user/LOGOUT', logout); + yield* takeLatest(userActions.LOGOUT, logout); } function* loginErrorWatch() { - yield* takeLatest('user/LOGIN_ERROR', loginError); + yield* takeLatest(userActions.LOGIN_ERROR, loginError); } function* watchLoadSavingsWithdraw() { - yield* takeLatest('user/LOAD_SAVINGS_WITHDRAW', loadSavingsWithdraw); + yield* takeLatest(userActions.LOAD_SAVINGS_WITHDRAW, loadSavingsWithdraw); } export function* watchRemoveHighSecurityKeys() { @@ -65,10 +69,10 @@ function* loadSavingsWithdraw() { const withdraws = List(fromJS(m).values()) .sort((a, b) => strCmp(a.get('complete'), b.get('complete'))) - yield put(user.actions.set({ + yield put(userActions.set({ key: 'savings_withdraws', value: withdraws, - })) + })); } const strCmp = (a, b) => a > b ? 1 : a < b ? -1 : 0 @@ -84,7 +88,7 @@ function* removeHighSecurityKeys({payload: {pathname}}) { // from getting logged out when they click on Permissions (which is really bad because that tab // disappears again). if(!highSecurityPage) - yield put(user.actions.removeHighSecurityKeys()) + yield put(userActions.removeHighSecurityKeys()); } /** @@ -94,7 +98,7 @@ function* removeHighSecurityKeys({payload: {pathname}}) { */ function* usernamePasswordLogin(action) { // Sets 'loading' while the login is taking place. The key generation can take a while on slow computers. - yield call(usernamePasswordLogin2, action) + yield call(usernamePasswordLogin2, action.payload) const current = yield select(state => state.user.get('current')) if(current) { const username = current.get('username') @@ -109,9 +113,9 @@ function* usernamePasswordLogin(action) { const clean = (value) => value == null || value === '' || /null|undefined/.test(value) ? undefined : value -function* usernamePasswordLogin2({payload: {username, password, saveLogin, +function* usernamePasswordLogin2({username, password, saveLogin, operationType /*high security*/, afterLoginRedirectToWelcome -}}) { +}) { // login, using saved password let feedURL = false; let autopost, memoWif, login_owner_pubkey, login_wif_owner_pubkey @@ -148,12 +152,12 @@ function* usernamePasswordLogin2({payload: {username, password, saveLogin, const account = yield call(getAccount, username) if (!account) { - yield put(user.actions.loginError({ error: 'Username does not exist' })) + yield put(userActions.loginError({ error: 'Username does not exist' })); return } //dmca user block if (username && DMCAUserList.includes(username)) { - yield put(user.actions.loginError({ error: translate('terms_violation') })) + yield put(userActions.loginError({ error: translate('terms_violation') })); return } @@ -184,29 +188,20 @@ function* usernamePasswordLogin2({payload: {username, password, saveLogin, if(!highSecurityLogin) { const accountName = account.get('name') authority = authority.set('active', 'none') - yield put(user.actions.setAuthority({accountName, auth: authority})) + yield put(userActions.setAuthority({ accountName, auth: authority })); } const fullAuths = authority.reduce((r, auth, type) => (auth === 'full' ? r.add(type) : r), Set()) if (!fullAuths.size) { localStorage.removeItem('autopost2') const owner_pub_key = account.getIn(['owner', 'key_auths', 0, 0]); - // const pub_keys = yield select(state => state.user.get('pub_keys_used')) - // serverApiRecordEvent('login_attempt', JSON.stringify({name: username, ...pub_keys, cur_owner: owner_pub_key})) - // FIXME pls parameterize opaque things like this into a constants file - // code like this requires way too much historical knowledge to - // understand. - if (owner_pub_key === 'STM7sw22HqsXbz7D2CmJfmMwt9rimtk518dRzsR1f8Cgw52dQR1pR') { - yield put(user.actions.loginError({ error: 'Hello. Your account may have been compromised. We are working on restoring an access to your account. Please send an email to support@steemit.com.' })) - return - } if(login_owner_pubkey === owner_pub_key || login_wif_owner_pubkey === owner_pub_key) { - yield put(user.actions.loginError({ error: 'owner_login_blocked' })) + yield put(userActions.loginError({ error: 'owner_login_blocked' })); } else if(!highSecurityLogin && hasActiveAuth) { - yield put(user.actions.loginError({ error: 'active_login_blocked' })) + yield put(userActions.loginError({ error: 'active_login_blocked' })); } else { const generated_type = password[0] === 'P' && password.length > 40; serverApiRecordEvent('login_attempt', JSON.stringify({name: username, login_owner_pubkey, owner_pub_key, generated_type})) - yield put(user.actions.loginError({ error: 'Incorrect Password' })) + yield put(userActions.loginError({ error: 'Incorrect Password' })); } return } @@ -231,7 +226,7 @@ function* usernamePasswordLogin2({payload: {username, password, saveLogin, posting_pubkey === owner_pubkey || posting_pubkey === active_pubkey ) { - yield put(user.actions.loginError({ error: 'This login gives owner or active permissions and should not be used here. Please provide a posting only login.' })) + yield put(userActions.loginError({ error: 'This login gives owner or active permissions and should not be used here. Please provide a posting only login.' })); localStorage.removeItem('autopost2') return } @@ -251,18 +246,26 @@ function* usernamePasswordLogin2({payload: {username, password, saveLogin, if(username) feedURL = '/@' + username + '/feed'; // Keep the posting key in RAM but only when not signing an operation. // No operation or the user has checked: Keep me logged in... - yield put(user.actions.setUser({username, private_keys, login_owner_pubkey, vesting_shares: account.get('vesting_shares'), - received_vesting_shares: account.get('received_vesting_shares'), - delegated_vesting_shares: account.get('delegated_vesting_shares')})) + yield put(userActions.setUser({ + username, + private_keys, + login_owner_pubkey, + vesting_shares: account.get('vesting_shares'), + received_vesting_shares: account.get('received_vesting_shares'), + delegated_vesting_shares: account.get('delegated_vesting_shares'), + })); } else { if(username) feedURL = '/@' + username + '/feed'; - yield put(user.actions.setUser({username, vesting_shares: account.get('vesting_shares'), + yield put(userActions.setUser({ + username, + vesting_shares: account.get('vesting_shares'), received_vesting_shares: account.get('received_vesting_shares'), - delegated_vesting_shares: account.get('delegated_vesting_shares')})) + delegated_vesting_shares: account.get('delegated_vesting_shares'), + })); } if (!autopost && saveLogin) - yield put(user.actions.saveLogin()); + yield put(userActions.saveLogin()); try { // const challengeString = yield serverApiLoginChallenge() @@ -341,7 +344,7 @@ function* saveLogin_localStorage() { } function* logout() { - yield put(user.actions.saveLoginConfirm(false)) // Just incase it is still showing + yield put(userActions.saveLoginConfirm(false)); // Just incase it is still showing if (process.env.BROWSER) localStorage.removeItem('autopost2') serverApiLogout(); @@ -355,7 +358,7 @@ function* loginError({payload: {/*error*/}}) { If the owner key was changed after the login owner key, this function will find the next owner key history record after the change and store it under user.previous_owner_authority. */ function* lookupPreviousOwnerAuthority({payload: {}}) { - const current = yield select(state => state.user.get('current')) + const current = yield select(state => state.user.getIn(['current'])) if(!current) return const login_owner_pubkey = current.get('login_owner_pubkey') @@ -387,11 +390,11 @@ function* lookupPreviousOwnerAuthority({payload: {}}) { return } // console.log('UserSage ---> previous_owner_authority', previous_owner_authority.toJS()) - yield put(user.actions.setUser({previous_owner_authority})) + yield put(userActions.setUser({previous_owner_authority})); } function* uploadImageWatch() { - yield* takeLatest('user/UPLOAD_IMAGE', uploadImage); + yield* takeLatest(userActions.UPLOAD_IMAGE, uploadImage); } function* uploadImage({payload: {file, dataUrl, filename = 'image.txt', progress}}) { diff --git a/src/app/redux/tests/global.test.js b/src/app/redux/tests/global.test.js index a55397619..05daa91c8 100644 --- a/src/app/redux/tests/global.test.js +++ b/src/app/redux/tests/global.test.js @@ -3,11 +3,9 @@ import chai, {expect} from 'chai'; import chaiImmutable from 'chai-immutable'; import Immutable, {Map} from 'immutable'; -import ReducerModule from '../GlobalReducer'; +import reducer, * as globalActions from '../GlobalReducer'; chai.use(chaiImmutable); -const {reducer, actions} = ReducerModule; - describe('global reducer', () => { it('should return empty state', () => { expect( @@ -19,7 +17,7 @@ describe('global reducer', () => { const state = Immutable.fromJS(require('./global.json')); //const action = {type: 'global/RECEIVE_STATE', payload: state}; expect( - reducer(undefined, actions.receiveState(state)) + reducer(undefined, globalActions.receiveState(state)) ).to.equal(state); }); }); diff --git a/src/shared/UniversalRender.jsx b/src/shared/UniversalRender.jsx index 82e4bf57a..8c37ad7d7 100644 --- a/src/shared/UniversalRender.jsx +++ b/src/shared/UniversalRender.jsx @@ -8,6 +8,7 @@ import { renderToString } from 'react-dom/server'; import { Router, RouterContext, match, applyRouterMiddleware, browserHistory } from 'react-router'; import { Provider } from 'react-redux'; import RootRoute from 'app/RootRoute'; +import * as appActions from 'app/redux/AppReducer'; import {createStore, applyMiddleware, compose} from 'redux'; import { useScroll } from 'react-router-scroll'; import createSagaMiddleware from 'redux-saga'; @@ -328,11 +329,11 @@ async function universalRender({location, initial_state, offchain, ErrorPage, ta offchain.server_location = location; server_store = createStore(rootReducer, { app: initial_state.app, global: onchain, offchain}); server_store.dispatch({type: '@@router/LOCATION_CHANGE', payload: {pathname: location}}); - server_store.dispatch({type: 'SET_USER_PREFERENCES', payload: userPreferences}); + server_store.dispatch(appActions.setUserPreferences(userPreferences)); if (offchain.account) { try { const notifications = await tarantool.select('notifications', 0, 1, 0, 'eq', offchain.account); - server_store.dispatch({type: 'UPDATE_NOTIFICOUNTERS', payload: notificationsArrayToMap(notifications)}); + server_store.dispatch(appActions.updateNotificounters(notificationsArrayToMap(notifications))); } catch(e) { console.warn('WARNING! cannot retrieve notifications from tarantool in universalRender:', e.message); } diff --git a/yarn.lock b/yarn.lock index 9df41c09b..d6055e88f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1467,13 +1467,6 @@ callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" -camel-case@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-1.2.2.tgz#1aca7c4d195359a2ce9955793433c6e5542511f2" - dependencies: - sentence-case "^1.1.1" - upper-case "^1.1.1" - camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -1497,7 +1490,7 @@ camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" -camelize@1.0.0, camelize@^1.0.0: +camelize@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" @@ -3163,12 +3156,6 @@ flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" -flux-standard-action@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/flux-standard-action/-/flux-standard-action-0.6.1.tgz#6f34211b94834ea1c3cc30f4e7afad3d0fbf71a2" - dependencies: - lodash.isplainobject "^3.2.0" - for-in@^0.1.3: version "0.1.8" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" @@ -3954,7 +3941,7 @@ ignore@^3.3.3: version "3.3.5" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.5.tgz#c4e715455f6073a8d7e5dae72d2fc9d71663dba6" -immutable@^3.7.4, immutable@^3.7.6, immutable@^3.8.1: +immutable@^3.7.4, immutable@^3.8.1: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" @@ -4808,10 +4795,6 @@ lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" -lodash._basefor@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" - lodash._basetostring@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" @@ -4906,14 +4889,6 @@ lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" -lodash.isplainobject@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz#9a8238ae16b200432960cd7346512d0123fbf4c5" - dependencies: - lodash._basefor "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.keysin "^3.0.0" - lodash.isplainobject@^4.0.4: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -4930,13 +4905,6 @@ lodash.keys@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash.keysin@^3.0.0: - version "3.0.8" - resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz#22c4493ebbedb1427962a54b445b2c8a767fb47f" - dependencies: - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - lodash.map@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" @@ -5051,10 +5019,6 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - lru-cache@2: version "2.7.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" @@ -6389,10 +6353,6 @@ querystring@0.2.0, querystring@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" -ramda@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.19.1.tgz#89c4ad697265ff6b1face9f286439e2520d6679c" - random-bytes@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" @@ -6798,17 +6758,6 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^0.4.2" -reduce-reducers@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/reduce-reducers/-/reduce-reducers-0.1.2.tgz#fa1b4718bc5292a71ddd1e5d839c9bea9770f14b" - -redux-actions@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/redux-actions/-/redux-actions-0.9.1.tgz#a72767654bc21424c3df3f6240780ffa8872783c" - dependencies: - flux-standard-action "^0.6.0" - reduce-reducers "^0.1.0" - redux-form@5.3.4: version "5.3.4" resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-5.3.4.tgz#0536ec71daf919fc6ec93c9b39a9581213715980" @@ -6819,16 +6768,6 @@ redux-form@5.3.4: is-promise "^2.1.0" react-lazy-cache "^3.0.1" -redux-modules@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/redux-modules/-/redux-modules-0.0.5.tgz#ab38e8c7bf52b41f04a2f140c48703a241cebd1f" - dependencies: - camel-case "^1.2.2" - camelize "^1.0.0" - immutable "^3.7.6" - ramda "^0.19.1" - redux-actions "^0.9.1" - redux-saga@^0.9.5: version "0.9.5" resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-0.9.5.tgz#f4bde5896e466932f758f86247b2fa6a4694ec2a" @@ -7244,12 +7183,6 @@ sendgrid@^4.0.1: mailparser "^0.6.1" sendgrid-rest "^2.3.0" -sentence-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-1.1.3.tgz#8034aafc2145772d3abe1509aa42c9e1042dc139" - dependencies: - lower-case "^1.1.1" - sequelize-cli@^2.3.1: version "2.8.0" resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-2.8.0.tgz#4304cce60e499169603f838dedbab421c9849e74" @@ -8069,10 +8002,6 @@ unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - url-loader@^0.5.9: version "0.5.9" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.9.tgz#cc8fea82c7b906e7777019250869e569e995c295" -- GitLab From 34b4f893a948d3a17fe7f73efe788a7b27cf0077 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Thu, 30 Nov 2017 15:06:48 -0500 Subject: [PATCH 111/399] go back to ignoring STEEM_API_ERROR see #2078 --- src/app/redux/AppReducer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/redux/AppReducer.js b/src/app/redux/AppReducer.js index db52827de..2d52a7ae3 100644 --- a/src/app/redux/AppReducer.js +++ b/src/app/redux/AppReducer.js @@ -45,7 +45,11 @@ export default function reducer(state = defaultState, action) { case '@@router/LOCATION_CHANGE': return state.set('location', { pathname: action.payload.pathname }); case STEEM_API_ERROR: - return state.set('error', action.error).set('loading', false); + // Until we figure out how to better handle these errors, let em slide. + // This action is the only part of the app that marks an error in state.app.error, + // and the only part of the app which pays attn to this part of the state is in App.jsx. + //return state.set('error', action.error).set('loading', false); + return state; case FETCH_DATA_BEGIN: return state.set('loading', true); case FETCH_DATA_END: -- GitLab From 7e100ee312341b9cb9c9e23b663b5fdefa93c276 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Mon, 4 Dec 2017 00:05:30 -0500 Subject: [PATCH 112/399] hotfix: add missing import bug fix for post editor image upload --- src/app/components/elements/ReplyEditor.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/components/elements/ReplyEditor.jsx b/src/app/components/elements/ReplyEditor.jsx index cb4d370df..777a4a60f 100644 --- a/src/app/components/elements/ReplyEditor.jsx +++ b/src/app/components/elements/ReplyEditor.jsx @@ -1,6 +1,7 @@ import React from 'react'; import reactForm from 'app/utils/ReactForm' import * as transactionActions from 'app/redux/TransactionReducer'; +import * as userActions from 'app/redux/UserReducer'; import MarkdownViewer from 'app/components/cards/MarkdownViewer' import CategorySelector from 'app/components/cards/CategorySelector' import {validateCategory} from 'app/components/cards/CategorySelector' -- GitLab From 7f328db2b7e805febb5ebba67d4403c0de36f73e Mon Sep 17 00:00:00 2001 From: roadscape Date: Mon, 4 Dec 2017 14:10:37 -0600 Subject: [PATCH 113/399] add blocktraders to BAL --- src/app/utils/BadActorList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index 92ae84daa..f0f24666e 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -110,6 +110,7 @@ bloocktrades bloctrades blocktradess blocktrade +blocktraders `.trim().split('\n'); export default list; -- GitLab From d04b9bc45304e2b3b28b34febffd0a82e3cd36ad Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Tue, 5 Dec 2017 12:46:31 -0500 Subject: [PATCH 114/399] actually run prettier closes #2100 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac66a7482..8795cb866 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "test:watch:all": "npm test -- --watch --watch-extensions jsx", "test:watch": "npm run mocha -- --watch --watch-extensions jsx", "eslint": "LIST=`git diff-index --name-only HEAD | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi", - "fmt": "prettier --config .prettierrc --write src", + "fmt": "prettier --config .prettierrc --write 'src/**/*.js*'", "production": "NODE_ENV=production node lib/server/index.js", "start": "NODE_ENV=development ./node_modules/babel-cli/bin/babel-node.js ./webpack/dev-server.js", "webpush": "./node_modules/babel-cli/bin/babel-node.js ./scripts/webpush_notify.js", -- GitLab From bc86bf69536359c0c53c6f4fb137219feae85a56 Mon Sep 17 00:00:00 2001 From: valzav Date: Thu, 19 Oct 2017 15:37:48 -0500 Subject: [PATCH 115/399] Remove babel-node from the webpack build cycle (fix #1825) --- package.json | 2 +- webpack/base.config.js | 10 +++++----- webpack/prod.config.js | 8 ++++---- webpack/utils/write-stats.js | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 8795cb866..5c1b80311 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "description": "steemit.com is the koa web server & middleware and react.js in-browser code for the world's first blockchain content + social media monetization platform!", "main": "index.js", "scripts": { - "build": "NODE_ENV=production ./node_modules/babel-cli/bin/babel-node.js ./node_modules/.bin/webpack --config ./webpack/prod.config.js; rm -rf ./lib; NODE_ENV=production babel --plugins transform-runtime,transform-inline-environment-variables src --out-dir lib -Dq", + "build": "NODE_ENV=production webpack --config ./webpack/prod.config.js; rm -rf ./lib; babel src --out-dir lib -Dq", "mocha": "NODE_ENV=test mocha ./mocha.setup.js", "test": "npm run mocha -- src/app/**/*.test.js src/shared/**/*.test.js", "test:watch:all": "npm test -- --watch --watch-extensions jsx", diff --git a/webpack/base.config.js b/webpack/base.config.js index 0997e426e..116f0de75 100644 --- a/webpack/base.config.js +++ b/webpack/base.config.js @@ -1,7 +1,7 @@ -import path from 'path'; -import webpack from 'webpack'; -import ExtractTextPlugin from 'extract-text-webpack-plugin'; -import writeStats from './utils/write-stats'; +const path = require('path'); +const webpack = require('webpack'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const writeStats = require('./utils/write-stats'); const Webpack_isomorphic_tools_plugin = require('webpack-isomorphic-tools/plugin'); const webpack_isomorphic_tools_plugin = @@ -32,7 +32,7 @@ const scss_loaders = [ } ] -export default { +module.exports = { entry: { app: ['babel-polyfill', './src/app/Main.js'], vendor: [ diff --git a/webpack/prod.config.js b/webpack/prod.config.js index 1111e109a..fddddf01d 100644 --- a/webpack/prod.config.js +++ b/webpack/prod.config.js @@ -1,8 +1,8 @@ -import webpack from 'webpack'; -import git from 'git-rev-sync'; -import baseConfig from './base.config'; +const webpack = require('webpack'); +const git = require('git-rev-sync'); +const baseConfig = require('./base.config'); -export default { +module.exports = { ...baseConfig, plugins: [ new webpack.DefinePlugin({ diff --git a/webpack/utils/write-stats.js b/webpack/utils/write-stats.js index 249c584fa..354e61af6 100644 --- a/webpack/utils/write-stats.js +++ b/webpack/utils/write-stats.js @@ -1,8 +1,8 @@ // borrowed from https://github.com/gpbl/isomorphic500/blob/master/webpack%2Futils%2Fwrite-stats.js -import fs from 'fs'; -import path from 'path'; +const fs = require('fs'); +const path = require('path'); -export default function (stats) { +module.exports = function (stats) { const publicPath = this.options.output.publicPath; const json = stats.toJson(); -- GitLab From 083e3a61e54fd581f12ce9ff633c1947ae259cec Mon Sep 17 00:00:00 2001 From: valzav Date: Thu, 19 Oct 2017 15:53:12 -0500 Subject: [PATCH 116/399] make more modules plain node compatible --- package.json | 4 ++-- scripts/webpush_notify.js | 6 +++--- src/db/tarantool.js | 6 +++--- webpack/dev-server.js | 6 +++--- webpack/dev.config.js | 10 +++++----- webpack/utils/start-koa.js | 11 +++++++---- 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 5c1b80311..d517d58ea 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ "eslint": "LIST=`git diff-index --name-only HEAD | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi", "fmt": "prettier --config .prettierrc --write 'src/**/*.js*'", "production": "NODE_ENV=production node lib/server/index.js", - "start": "NODE_ENV=development ./node_modules/babel-cli/bin/babel-node.js ./webpack/dev-server.js", - "webpush": "./node_modules/babel-cli/bin/babel-node.js ./scripts/webpush_notify.js", + "start": "NODE_ENV=development babel-node ./webpack/dev-server.js", + "webpush": "node ./scripts/webpush_notify.js", "checktranslations": "node scripts/check_translations.js" }, "author": "Steemit, Inc.", diff --git a/scripts/webpush_notify.js b/scripts/webpush_notify.js index 7d459fbe8..8c0090bdc 100644 --- a/scripts/webpush_notify.js +++ b/scripts/webpush_notify.js @@ -1,6 +1,6 @@ -import config from 'config'; -import webPush from 'web-push'; -import Tarantool from '../src/db/tarantool'; +const config = require('config'); +const webPush = require('web-push'); +const Tarantool = require('../src/db/tarantool'); webPush.setGCMAPIKey(config.get('notify.gcm_key')); diff --git a/src/db/tarantool.js b/src/db/tarantool.js index be397a90a..d07c9c496 100644 --- a/src/db/tarantool.js +++ b/src/db/tarantool.js @@ -1,5 +1,5 @@ -import config from 'config'; -import TarantoolDriver from 'tarantool-driver'; +const config = require('config'); +const TarantoolDriver = require('tarantool-driver'); let instance = null; @@ -67,4 +67,4 @@ Tarantool.instance = function () { return instance; }; -export default Tarantool; +module.exports = Tarantool; diff --git a/webpack/dev-server.js b/webpack/dev-server.js index 72b992360..29a4b367a 100644 --- a/webpack/dev-server.js +++ b/webpack/dev-server.js @@ -5,10 +5,10 @@ if(!fs.existsSync('tmp')) process.env.BABEL_ENV = 'browser'; process.env.NODE_ENV = 'development'; -import Koa from 'koa'; -import webpack from 'webpack'; +const Koa = require('koa'); +const webpack = require('webpack'); -import webpackDevConfig from './dev.config'; +const webpackDevConfig = require('./dev.config'); const app = new Koa(); const compiler = webpack(webpackDevConfig); diff --git a/webpack/dev.config.js b/webpack/dev.config.js index 360115391..5ba983d59 100644 --- a/webpack/dev.config.js +++ b/webpack/dev.config.js @@ -1,12 +1,12 @@ -import webpack from 'webpack'; -import git from 'git-rev-sync'; -import baseConfig from './base.config'; -import startKoa from './utils/start-koa'; +const webpack = require('webpack'); +const git = require('git-rev-sync'); +const baseConfig = require('./base.config'); +const startKoa = require('./utils/start-koa'); // var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // baseConfig.plugins.push(new BundleAnalyzerPlugin()); -export default { +module.exports = { ...baseConfig, devtool: 'cheap-module-eval-source-map', output: { diff --git a/webpack/utils/start-koa.js b/webpack/utils/start-koa.js index 0fbb6458e..f871e92ee 100644 --- a/webpack/utils/start-koa.js +++ b/webpack/utils/start-koa.js @@ -1,6 +1,6 @@ -import cp from 'child_process'; -import path from 'path'; -import watch from 'node-watch'; +const cp = require('child_process'); +const path = require('path'); +const watch = require('node-watch'); let server; let started; @@ -50,4 +50,7 @@ const startServer = () => { // kill server on exit process.on('exit', () => server.kill('SIGTERM')); -export default () => !server ? startServer() : () => ({}); + +module.exports = function () { + return !server ? startServer() : () => ({}); +}; -- GitLab From be96ca513dc6c11c29bc586d1da102ed26aa8358 Mon Sep 17 00:00:00 2001 From: Park Date: Wed, 6 Dec 2017 11:56:38 +0000 Subject: [PATCH 117/399] Add more Korean translations and fix typo, unnatural translations --- src/app/locales/ko.json | 227 ++++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 113 deletions(-) diff --git a/src/app/locales/ko.json b/src/app/locales/ko.json index c5085c60b..61e456f9d 100644 --- a/src/app/locales/ko.json +++ b/src/app/locales/ko.json @@ -1,7 +1,7 @@ { "g": { - "age": "나이", - "amount": "Amount", + "age": "시간", + "amount": "양", "and": "and", "are_you_sure": "Are you sure?", "ask": "Ask", @@ -11,7 +11,7 @@ "blog": "블로그", "browse": "Browse", "buy": "구매", - "buy_or_sell": "Buy or Sell", + "buy_or_sell": "구입/판매", "by": "by", "cancel": "취소", "change_password": "비밀번호 변경", @@ -19,7 +19,7 @@ "clear": "확인", "close": "닫기", "collapse_or_expand": "닫기/펼치기", - "comments": "나의댓글", + "comments": "나의 댓글", "confirm": "확인", "convert": "변환", "date": "날자", @@ -32,20 +32,20 @@ "for": " for ", "from": " from ", "go_back": "Back", - "hide": "Hide", + "hide": "숨기기", "in": "in", "in_reply_to": "in reply to", "insufficient_balance": "잔고 부족", - "invalid_amount": "Invalid amount", - "joined": "Joined", - "loading": "Loading", + "invalid_amount": "잘못된 금액", + "joined": "가입일", + "loading": "로딩중..", "login": "로그인", "logout": "로그아웃", "memo": "메모", "mute": "뮤트", - "myblog": "내블로그", - "mycomments": "나의댓글", - "myreplies": "내게달린댓글", + "myblog": "내 블로그", + "mycomments": "나의 댓글", + "myreplies": "내게 달린 댓글", "new": "최신글", "newer": "Newer", "next": "Next", @@ -64,8 +64,8 @@ "powered_up_100": "Powered Up 100%%", "preview": "Preview", "previous": "Previous", - "price": "Price", - "print": "Print", + "price": "가격", + "print": "출력", "promote": "홍보", "promoted": "홍보글", "re": "RE", @@ -73,10 +73,10 @@ "recent_password": "Recent Password", "receive": "Receive ", "remove": "Remove", - "remove_vote": "보팅취소", + "remove_vote": "보팅 취소", "replied_to": "replied to %(account)s", - "replies": "받은댓글", - "reply": "댓글달기", + "replies": "받은 댓글", + "reply": "댓글 달기", "reply_count": { "zero": "댓글 없음", "one": "하나의 댓글", @@ -90,7 +90,7 @@ "save": "Save", "saved": "Saved", "search": "Search", - "sell": "Sell", + "sell": "판매", "settings": "설정", "share_this_post": "Share this post", "show": "Show", @@ -100,12 +100,12 @@ "submit": "Submit", "power_up": "파워 업", "submit_a_story": "새글", - "tag": "Tag", + "tag": "태그", "to": " to ", "topics": "Topics", - "toggle_nightmode": "야간모드전환", - "all_tags": "All tags", - "transfer": "Transfer ", + "toggle_nightmode": "야간모드", + "all_tags": "전체 태그", + "transfer": "송금 ", "trending_topics": "Trending Topics", "type": "Type", "unfollow": "언팔로우", @@ -115,8 +115,8 @@ "upvote_post": "이 글에 보팅", "username": "사용자 이름", "version": "버전", - "vote": "보트", - "votes": "보트", + "vote": "보팅", + "votes": "보팅", "wallet": "지갑", "warning": "경고", "yes": "예", @@ -126,42 +126,42 @@ "account_not_found": "계정을 찾을 수 없습니다", "this_is_wrong_password": "잘못된 비밀번호 입니다", "do_you_need_to": "Do you need to", - "account_name": "Account Name", + "account_name": "계정 이름", "recover_your_account": "recover your account", "reset_usernames_password": "Reset %(username)s's Password", "this_will_update_usernames_authtype_key": "This will update %(username)s %(authType)s key", "passwords_do_not_match": "Passwords do not match", "you_need_private_password_or_key_not_a_public_key": "You need a private password or key (not a public key)", "the_rules_of_APP_NAME": { - "one": "The first rule of %(APP_NAME)s is: Do not lose your password.", - "second": "The second rule of %(APP_NAME)s is: Do not lose your password.", - "third": "The third rule of %(APP_NAME)s is: We cannot recover your password.", - "fourth": "The fourth rule: If you can remember the password, it's not secure.", - "fifth": "The fifth rule: Use only randomly-generated passwords.", - "sixth": "The sixth rule: Do not tell anyone your password.", - "seventh": "The seventh rule: Always back up your password." + "one": "%(APP_NAME)s 첫번째 규칙: 비밀번호를 잃어버리지 마세요.", + "second": "%(APP_NAME)s 두번째 규칙: 비밀번호를 잃어버리지 마세요.", + "third": "%(APP_NAME)s 세번째 규칙: 비밀번호를 복구해 줄 방법이 없습니다.", + "fourth": "네번째 규칙: 기억할수 있는 비밀번호는 안전하지 않은 비밀번호 입니다.", + "fifth": "다섯번째 규칙: 무작위로 생성된 비밀번호만을 사용하세요.", + "sixth": "여섯번째 규칙: 비밀번호는 혼자만 간직하세요.", + "seventh": "일곱번째 규칙: 비밀전호를 반드시 백업해 두세요." }, - "recover_password": "Recover Account", - "current_password": "Current Password", - "generated_password": "Generated Password", - "backup_password_by_storing_it": "Back it up by storing in your password manager or a text file", + "recover_password": "계정 복구", + "current_password": "현재 비밀번호", + "generated_password": "생성된 비밀번호", + "backup_password_by_storing_it": "반드시 패스워드 관리 프로그램이나 메모장 등을 이용해서 보관하세요.", "enter_account_show_password": "Enter a valid account name to show the password", "click_to_generate_password": "Click to generate password", "re_enter_generate_password": "Re-enter Generated Password", "understand_that_APP_NAME_cannot_recover_password": "I understand that %(APP_NAME)s cannot recover lost passwords", - "i_saved_password": "I have securely saved my generated password", + "i_saved_password": "안전한곳에 비밀번호를 백업 해 두었습니다", "update_password": "Update Password", "confirm_password": "Confirm Password", "account_updated": "Account Updated", "password_must_be_characters_or_more": "Password must be %(amount)s characters or more", "need_password_or_key": "You need a private password or key (not a public key)", "login_to_see_memo": "login to see memo", - "new_password": "New Password", - "incorrect_password": "Incorrect password", - "username_does_not_exist": "Username does not exist", - "account_name_should_start_with_a_letter": "Account name should start with a letter.", - "account_name_should_be_shorter": "Account name should be shorter.", - "account_name_should_be_longer": "Account name should be longer.", + "new_password": "새 비밀번호", + "incorrect_password": "잘못된 비밀번호", + "username_does_not_exist": "존재하지 않는 사용자 계정", + "account_name_should_start_with_a_letter": "계정명은 반드시 알파벳으로 시작해야 합니다.", + "account_name_should_be_shorter": "계정명이 너무 깁니다.", + "account_name_should_be_longer": "계정명이 너무 짧습니다.", "account_name_should_have_only_letters_digits_or_dashes": "Account name should have only letters, digits, or dashes.", "cannot_increase_reward_of_post_within_the_last_minute_before_payout": "Cannot increase reward of post within the last minute before payout", "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": "vote currently exists, user must be indicate a desire to reject witness", @@ -181,7 +181,7 @@ "sorry_your_reddit_account_doesnt_have_enough_karma": "Sorry, your Reddit account doesn't have enough Reddit Karma to qualify for a free sign up. Please add your email for a place on the waiting list", "register_with_facebook": "Register with Facebook", "or_click_the_button_below_to_register_with_facebook": "Or click the button below to register with Facebook", - "server_returned_error": "server returned error", + "server_returned_error": "서버 오류", "APP_NAME_support": "%(APP_NAME)s Support", "please_email_questions_to": "Please email your questions to", "next_7_strings_single_block": { @@ -195,7 +195,7 @@ "show_less": "Show fewer", "value_posts": "low value posts" }, - "read_only_mode": "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", + "read_only_mode": "서버 점검으로 인한 읽기 전용 모드입니다. 불편함을 드려 죄송합니다.", "tags_and_topics": "태그", "show_more_topics": "모든 태그 보기", "basic": "Basic", @@ -217,57 +217,58 @@ } }, "navigation": { - "about": "About", - "explore": "Explore", - "APP_NAME_whitepaper": "%(APP_NAME)s Whitepaper", - "buy_LIQUID_TOKEN": "Buy %(LIQUID_TOKEN)s", - "sell_LIQUID_TOKEN": "Sell %(LIQUID_TOKEN)s", - "currency_market": "Currency Market", - "stolen_account_recovery": "Stolen Accounts Recovery", - "change_account_password": "Change Account Password", - "witnesses": "Witnesses", - "vote_for_witnesses": "Vote for Witnesses", - "privacy_policy": "Privacy Policy", - "terms_of_service": "Terms of Service", - "sign_up": "Join", - "learn_more": "Learn More", - "welcome": "Welcome", - "faq": "FAQ", - "shop": "The Steemit Shop", - "chat": "Steemit Chat", - "app_center": "Steemit App Center", - "api_docs": "Steemit API Docs", - "bluepaper": "Steem Bluepaper", - "whitepaper": "Steem Whitepaper", - "intro_tagline": "머니 톡스", - "intro_paragraph": "Your voice is worth something. Join the community that pays you to post and curate high quality content." + "about": "스팀?", + "explore": "태그별 현황", + "APP_NAME_whitepaper": "%(APP_NAME)s 백서", + "buy_LIQUID_TOKEN": "%(LIQUID_TOKEN)s 구매", + "sell_LIQUID_TOKEN": "%(LIQUID_TOKEN)s 판매", + "currency_market": "거래소", + "stolen_account_recovery": "도난 계정 복구", + "change_account_password": "비밀번호 변경", + "witnesses": "증인", + "vote_for_witnesses": "증인 투표", + "privacy_policy": "개인정보 방침", + "terms_of_service": "이용 약관", + "sign_up": "가입", + "learn_more": "더 알아보기", + "welcome": "환영합니다!", + "faq": "자주 묻는 질문", + "shop": "스팀잇 스토어", + "chat": "스팀잇 채팅", + "app_center": "스팀잇 앱 센터", + "api_docs": "스팀잇 API 문서", + "bluepaper": "스팀 블루페이퍼", + "smt_whitepaper": "SMT 화이트페이퍼", + "whitepaper": "스팀 화이트페이퍼", + "intro_tagline": "생각의 가치", + "intro_paragraph": "당신의 생각과 글은 소중합니다. 스팀잇은, 고급 컨텐츠 생산자들과 큐레이터들에게 투명한 금전적 보상을 지원합니다. 지금 참여하세요." }, "main_menu": { "hot": "인기글", "trending": "추천글" }, "reply_editor": { - "shorten_title": "Shorten title", - "exceeds_maximum_length": "Exceeds maximum length (%(maxKb)sKB)", + "shorten_title": "짧은 제목", + "exceeds_maximum_length": "최대 용량 초과 (%(maxKb)sKB)", "including_the_category": " (including the category '%(rootCategory)s')", "use_limited_amount_of_tags": "You have %(tagsLength)s tags total%(includingCategory)s. Please use only 5 in your post and category line.", "are_you_sure_you_want_to_clear_this_form": "Are you sure you want to clear this form?", "uploading": "Uploading", - "draft_saved": "Draft saved.", - "editor": "Editor", + "draft_saved": "임시 저장.", + "editor": "에디터", "insert_images_by_dragging_dropping": "이미지 추가를 위해서는 드래그&드롭, ", - "pasting_from_the_clipboard": "클립보드에서 분여넣기, ", + "pasting_from_the_clipboard": "클립보드에서 붙여넣기, ", "selecting_them": "혹은 직접 업로드 하세요.", "image_upload": "사진 업로드", - "power_up_100": "100%% 스팀파워로 받음", - "default_50_50": "스팀파워 50%% + 스팀달러 50%%)", + "power_up_100": "100%% 스팀파워로 수령", + "default_50_50": "스팀파워 50%% + 스팀달러 50%%", "decline_payout": "수익을 스팀 커뮤니티에 기부", - "check_this_to_auto_upvote_your_post": "Check this to auto-upvote your post", - "markdown_styling_guide": "Markdown Styling Guide", + "check_this_to_auto_upvote_your_post": "이 글에 자동으로 업보팅", + "markdown_styling_guide": "Markdown 설명서", "or_by": "or by", - "title": "Title", - "update_post": "Update Post", - "markdown_not_supported": "Markdown is not supported here" + "title": "제목", + "update_post": "저장", + "markdown_not_supported": "Markdown은 지원되지 않습니다." }, "category_selector_jsx": { "tag_your_story": "최대 5개의 태그를 입력하세요. 메인 태그는 가장 먼저 입력하세요.", @@ -283,13 +284,13 @@ }, "postfull_jsx": { "this_post_is_not_available_due_to_a_copyright_claim": "This post is not available due to a copyright claim.", - "share_on_facebook": "Share on Facebook", - "share_on_twitter": "Share on Twitter", - "share_on_linkedin": "Share on Linkedin", - "recent_password": "Recent Password", + "share_on_facebook": "Facebook에 공유", + "share_on_twitter": "Twitter에 공유", + "share_on_linkedin": "Linkedin에 공유", + "recent_password": "최근 비밀번호", "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": "In 3.5 days, convert %(amount)s %(DEBT_TOKEN)s into %(LIQUID_TOKEN)s", - "view_the_full_context": "View the full context", - "view_the_direct_parent": "View the direct parent", + "view_the_full_context": "전체 글 보기", + "view_the_direct_parent": "상위 댓글 보기", "you_are_viewing_a_single_comments_thread_from": "You are viewing a single comment's thread from" }, "market_jsx": { @@ -345,8 +346,8 @@ "looks_like_you_havent_posted_anything_yet": "Looks like you haven't posted anything yet.", "create_a_post": "Create a Post", "explore_trending_articles": "Explore Trending Articles", - "read_the_quick_start_guide": "Read The Quick Start Guide", - "browse_the_faq": "Browse The FAQ", + "read_the_quick_start_guide": "빠른 시작 가이드", + "browse_the_faq": "자주 찾는 질문", "followers": "팔로워", "this_is_users_reputations_score_it_is_based_on_history_of_votes": "This is %(name)s's reputation score.\n\nThe reputation score is based on the history of votes received by the account, and is used to hide low quality content.", "follower_count": { @@ -375,7 +376,7 @@ }, "post_jsx": { "now_showing_comments_with_low_ratings": "Now showing comments with low ratings", - "sort_order": "Sort Order", + "sort_order": "정렬 순서", "comments_were_hidden_due_to_low_ratings": "Comments were hidden due to low ratings" }, "voting_jsx": { @@ -398,8 +399,8 @@ "confirm_flag": "Confirm Flag", "and_more": "and %(count)s more", "votes_plural": { - "one": "%(count)s 보트", - "other": "%(count)s 보트" + "one": "%(count)s 보팅", + "other": "%(count)s 보팅" } }, "witnesses_jsx": { @@ -454,16 +455,16 @@ "converttosteem_jsx": { "your_existing_DEBT_TOKEN_are_liquid_and_transferable": "Your existing %(DEBT_TOKEN)s are liquid and transferable. Instead you may wish to trade %(DEBT_TOKEN)s directly in this site under %(link)s or transfer to an external market.", "this_is_a_price_feed_conversion": "This is a price feed conversion. The 3.5 day delay is necessary to prevent abuse from gaming the price feed average", - "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", + "convert_to_LIQUID_TOKEN": "%(LIQUID_TOKEN)s 으로 전환", "DEBT_TOKEN_will_be_unavailable": "This action will take place 3.5 days from now and can not be canceled. These %(DEBT_TOKEN)s will immediately become unavailable" }, "tips_js": { - "liquid_token": "Tradeable tokens that may be transferred anywhere at anytime.
    %(LIQUID_TOKEN)s can be converted to %(VESTING_TOKEN)s in a process called powering up.", - "influence_token": "Influence tokens which give you more control over post payouts and allow you to earn on curation rewards.", - "estimated_value": "The estimated value is based on an average value of %(LIQUID_TOKEN)s in US dollars.", - "non_transferable": "%(VESTING_TOKEN)s is non-transferable and requires 3 months (13 payments) to convert back to %(LIQUID_TOKEN)s.", + "liquid_token": "교환과 송금이 가능한 토큰입니다.
    파워업을 통해 %(LIQUID_TOKEN)s을 %(VESTING_TOKEN)s으로 전환 하실 수 있습니다.", + "influence_token": "스팀잇에서의 영향력을 의미합니다. 더 높은 보팅과 더 많은 큐레이션 보상을 받을 수 있습니다.", + "estimated_value": "%(LIQUID_TOKEN)s의 평균 가격을 US달러로 환산한 기준입니다.", + "non_transferable": "%(VESTING_TOKEN)s는 전송이 불가능하며, 다시 %(LIQUID_TOKEN)s으로 전환하기 위해서는 3달이 소요됩니다. (13회로 나누어 전환 됩니다)", "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": "Converted %(VESTING_TOKEN)s can be sent to yourself or someone else but can not transfer again without converting back to %(LIQUID_TOKEN)s.", - "part_of_your_steem_power_is_currently_delegated": "Part of your STEEM POWER is currently delegated to you. Delegation is donated for influence or to help new users perform actions on steemit. Your delegation amount can fluctuate." + "part_of_your_steem_power_is_currently_delegated": "STEEM POWER 를 임대 받고 있습니다. 임대 받는 양은 가입 시기에 따라 가변적입니다." }, "promote_post_jsx": { "promote_post": "Promote Post", @@ -500,7 +501,7 @@ "to_savings": "to savings", "from_savings": "from savings", "cancel_transfer_from_savings": "Cancel transfer from savings", - "stop_power_down": "Stop power down", + "stop_power_down": "파워 다운 중지", "start_power_down_of": "Start power down of", "receive_interest_of": "Receive interest of" }, @@ -583,8 +584,8 @@ "location_is_too_long": "너무 긴 지역명입니다", "website_url_is_too_long": "너무 긴 웹사이트 URL입니다", "public_profile_settings": "공개 프로파일 설정", - "private_post_display_settings": "Private Post Display Settings", - "not_safe_for_work_nsfw_content": "Not safe for work (NSFW) content", + "private_post_display_settings": "컨텐츠 표시 설정", + "not_safe_for_work_nsfw_content": "선정성 및 폭력성 컨텐츠", "always_hide": "항상 숨기기", "always_warn": "항상 경고하기", "always_show": "항상 보이기", @@ -618,25 +619,25 @@ "userwallet_jsx": { "conversion_complete_tip": "Will complete on", "in_conversion": "%(amount)s in conversion", - "transfer_to_savings": "Transfer to Savings", - "power_up": "Power Up", - "power_down": "Power Down", - "market": "Market", - "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", - "withdraw_LIQUID_TOKEN": "Withdraw %(LIQUID_TOKEN)s", - "withdraw_DEBT_TOKENS": "Withdraw %(DEBT_TOKENS)s", + "transfer_to_savings": "금고로 전송", + "power_up": "파워 업", + "power_down": "파워 다운", + "market": "거래소", + "convert_to_LIQUID_TOKEN": "%(LIQUID_TOKEN)s 으로 전환", + "withdraw_LIQUID_TOKEN": "%(LIQUID_TOKEN)s 출금", + "withdraw_DEBT_TOKENS": "%(DEBT_TOKENS)s 출금", "tokens_worth_about_1_of_LIQUID_TICKER": "Tokens worth about $1.00 of %(LIQUID_TICKER)s, currently collecting %(sbdInterest)s%% APR.", - "savings": "SAVINGS", - "estimated_account_value": "Estimated Account Value", + "savings": "금고", + "estimated_account_value": "추정 자산가치", "next_power_down_is_scheduled_to_happen": "The next power down is scheduled to happen", - "transfers_are_temporary_disabled": "Transfers are temporary disabled.", - "history": "HISTORY", + "transfers_are_temporary_disabled": "현재 일시적인 이유로 송금이 불가능합니다.", + "history": "거래 내역", "redeem_rewards": "내 지갑으로 입금하기", "buy_steem_or_steem_power": "STEEM, STEEM POWER 구입" }, "powerdown_jsx": { - "power_down": "파워다운", - "amount": "Amount", + "power_down": "파워 다운", + "amount": "목표량", "already_power_down": "You are already powering down %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s paid out so far). Note that if you change the power down amount the payout schedule will reset.", "delegating": "You are delegating %(AMOUNT)s %(LIQUID_TICKER)s. That amount is locked up and not available to power down until the delegation is removed and a full reward period has passed.", "per_week": "That's ~%(AMOUNT)s %(LIQUID_TICKER)s per week.", -- GitLab From d93fabf032351c92a25eb1dab51c7820387b2ab3 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Tue, 5 Dec 2017 22:12:20 -0500 Subject: [PATCH 118/399] remove pre-commit package --- package.json | 1 - yarn.lock | 21 +-------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/package.json b/package.json index d517d58ea..2cd0fd07b 100644 --- a/package.json +++ b/package.json @@ -169,7 +169,6 @@ "koa-webpack-hot-middleware": "^1.0.3", "mocha": "^2.4.5", "node-watch": "^0.5.5", - "pre-commit": "^1.2.2", "prettier": "1.8.2", "react-addons-perf": "15.4.2", "react-addons-test-utils": "15.4.2", diff --git a/yarn.lock b/yarn.lock index 624f9fe7e..b2403c471 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5720,10 +5720,6 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-shim@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" - os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -6198,14 +6194,6 @@ postinstall-build@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/postinstall-build/-/postinstall-build-5.0.1.tgz#b917a9079b26178d9a24af5a5cd8cb4a991d11b9" -pre-commit@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6" - dependencies: - cross-spawn "^5.0.1" - spawn-sync "^1.0.15" - which "1.2.x" - prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -7377,13 +7365,6 @@ sparkles@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" -spawn-sync@^1.0.15: - version "1.0.15" - resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" - dependencies: - concat-stream "^1.4.7" - os-shim "^0.1.2" - spdx-correct@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" @@ -8284,7 +8265,7 @@ which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@1, which@1.2.x, which@^1.2.12, which@^1.2.9: +which@1, which@^1.2.12, which@^1.2.9: version "1.2.14" resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" dependencies: -- GitLab From b3e67d4c0f970f8c5d6446b722f7200f37519781 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Tue, 5 Dec 2017 22:17:51 -0500 Subject: [PATCH 119/399] add packages & command for running prettier as pre-commit hook --- package.json | 6 + yarn.lock | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 363 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2cd0fd07b..c9b357996 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test:watch": "npm run mocha -- --watch --watch-extensions jsx", "eslint": "LIST=`git diff-index --name-only HEAD | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi", "fmt": "prettier --config .prettierrc --write 'src/**/*.js*'", + "precommit": "lint-staged", "production": "NODE_ENV=production node lib/server/index.js", "start": "NODE_ENV=development babel-node ./webpack/dev-server.js", "webpush": "node ./scripts/webpush_notify.js", @@ -164,9 +165,11 @@ "eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-react": "^7.4.0", "extract-text-webpack-plugin": "^3.0.0", + "husky": "^0.14.3", "jsdom": "^9.8.0", "koa-webpack-dev-middleware": "^1.1.0", "koa-webpack-hot-middleware": "^1.0.3", + "lint-staged": "^6.0.0", "mocha": "^2.4.5", "node-watch": "^0.5.5", "prettier": "1.8.2", @@ -185,5 +188,8 @@ "engines": { "node": ">=8.7.0", "npm": ">=5.4.2" + }, + "lint-staged": { + "src/**/*.js*": ["prettier --write", "git add"] } } diff --git a/yarn.lock b/yarn.lock index b2403c471..c2ff4586f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -121,6 +121,10 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" +ansi-escapes@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + ansi-escapes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" @@ -141,12 +145,16 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.1.0: +ansi-styles@^3.1.0, ansi-styles@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" dependencies: color-convert "^1.9.0" +any-observable@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.2.0.tgz#c67870058003579009083f54ac0abafb5c33d242" + any-promise@^1.0.0, any-promise@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -158,6 +166,10 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" +app-root-path@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.0.1.tgz#cd62dcf8e4fd5a417efc664d2e5b10653c651b46" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -1552,6 +1564,14 @@ chalk@^2.0.0, chalk@^2.1.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^2.0.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + cheerio@^0.22.0: version "0.22.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" @@ -1588,6 +1608,10 @@ chokidar@^1.6.1, chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" +ci-info@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.2.tgz#03561259db48d0474c8bdc90f5b47b068b6bbfb4" + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -1624,12 +1648,29 @@ cli-color@~1.2.0: memoizee "^0.4.3" timers-ext "0.1" +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" dependencies: restore-cursor "^2.0.0" +cli-spinners@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -1905,6 +1946,15 @@ core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +cosmiconfig@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-3.1.0.tgz#640a94bf9847f321800403cd273af60665c73397" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^3.0.0" + require-from-string "^2.0.1" + counterpart@^0.17.6: version "0.17.9" resolved "https://registry.yarnpkg.com/counterpart/-/counterpart-0.17.9.tgz#0379e4d090b9efdb29c636a607c52e35734f5f13" @@ -2158,6 +2208,10 @@ dashify@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/dashify/-/dashify-0.2.2.tgz#6a07415a01c91faf4a32e38d9dfba71f61cb20fe" +date-fns@^1.27.2: + version "1.29.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" + date-names@^0.1.7: version "0.1.10" resolved "https://registry.yarnpkg.com/date-names/-/date-names-0.1.10.tgz#62f8d9332295044657f3ce7616d8a34021f8ef2b" @@ -2182,7 +2236,7 @@ debug@2.2.0: dependencies: ms "0.7.1" -debug@^3.0.1: +debug@^3.0.1, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -2192,6 +2246,10 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + deep-copy@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/deep-copy/-/deep-copy-1.4.0.tgz#f181ef0249bc83bbc5bb866d95c7d565acf45e8b" @@ -2489,6 +2547,10 @@ electron-to-chromium@^1.2.7: version "1.3.26" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.26.tgz#996427294861a74d9c7c82b9260ea301e8c02d66" +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -2559,7 +2621,7 @@ errno@^0.1.3: dependencies: prr "~0.0.0" -error-ex@^1.2.0: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" dependencies: @@ -2878,6 +2940,22 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -3021,6 +3099,13 @@ fbjs@^0.8.1, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9, fbjs@~0: setimmediate "^1.0.5" ua-parser-js "^0.7.9" +figures@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -3094,6 +3179,10 @@ find-index@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" +find-parent-dir@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -3358,6 +3447,10 @@ get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" +get-own-enumerable-property-symbols@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-2.0.1.tgz#5c4ad87f2834c4b9b4e84549dc1e0650fb38c24b" + get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" @@ -3907,6 +4000,14 @@ humanize-number@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/humanize-number/-/humanize-number-0.0.2.tgz#11c0af6a471643633588588048f1799541489c18" +husky@^0.14.3: + version "0.14.3" + resolved "https://registry.yarnpkg.com/husky/-/husky-0.14.3.tgz#c69ed74e2d2779769a17ba8399b54ce0b63c12c3" + dependencies: + is-ci "^1.0.10" + normalize-path "^1.0.0" + strip-indent "^2.0.0" + iconv-lite@0.4.13, iconv-lite@~0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" @@ -3966,6 +4067,10 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -4109,10 +4214,20 @@ is-callable@^1.1.1, is-callable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" +is-ci@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" + dependencies: + ci-info "^1.0.0" + is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -4131,6 +4246,10 @@ is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + is-finite@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" @@ -4157,6 +4276,12 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + is-my-json-valid@^2.12.0, is-my-json-valid@^2.12.4: version "2.16.1" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" @@ -4178,6 +4303,16 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + +is-observable@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-0.2.0.tgz#b361311d83c6e5d726cabf5e250b0237106f5ae2" + dependencies: + symbol-observable "^0.2.2" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -4226,6 +4361,10 @@ is-regex@^1.0.4: dependencies: has "^1.0.1" +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + is-relative@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" @@ -4322,6 +4461,19 @@ jade@0.26.3: commander "0.6.1" mkdirp "0.3.0" +jest-get-type@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23" + +jest-validate@^21.1.0: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7" + dependencies: + chalk "^2.0.1" + jest-get-type "^21.2.0" + leven "^2.1.0" + pretty-format "^21.2.1" + jquery-sortable@~0.9.12: version "0.9.13" resolved "https://registry.yarnpkg.com/jquery-sortable/-/jquery-sortable-0.9.13.tgz#1cbfb655013a0747370571f06e22f524a00ffba2" @@ -4353,7 +4505,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@^3.9.1: +js-yaml@^3.9.0, js-yaml@^3.9.1: version "3.10.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" dependencies: @@ -4709,6 +4861,10 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -4740,6 +4896,79 @@ liftoff@^2.1.0: rechoir "^0.6.2" resolve "^1.1.7" +lint-staged@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-6.0.0.tgz#7ab7d345f2fe302ff196f1de6a005594ace03210" + dependencies: + app-root-path "^2.0.0" + chalk "^2.1.0" + commander "^2.11.0" + cosmiconfig "^3.1.0" + debug "^3.1.0" + dedent "^0.7.0" + execa "^0.8.0" + find-parent-dir "^0.3.0" + is-glob "^4.0.0" + jest-validate "^21.1.0" + listr "^0.13.0" + lodash "^4.17.4" + log-symbols "^2.0.0" + minimatch "^3.0.0" + npm-which "^3.0.1" + p-map "^1.1.1" + path-is-inside "^1.0.2" + pify "^3.0.0" + staged-git-files "0.0.4" + stringify-object "^3.2.0" + +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + +listr-update-renderer@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7" + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^1.0.2" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +listr@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.13.0.tgz#20bb0ba30bae660ee84cc0503df4be3d5623887d" + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + figures "^1.7.0" + indent-string "^2.1.0" + is-observable "^0.2.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.4.0" + listr-verbose-renderer "^0.4.0" + log-symbols "^1.0.2" + log-update "^1.0.2" + ora "^0.2.3" + p-map "^1.1.1" + rxjs "^5.4.2" + stream-to-observable "^0.2.0" + strip-ansi "^3.0.1" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -4990,6 +5219,25 @@ lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + dependencies: + chalk "^1.0.0" + +log-symbols@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.1.0.tgz#f35fa60e278832b538dc4dddcbb478a45d3e3be6" + dependencies: + chalk "^2.0.1" + +log-update@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" + dependencies: + ansi-escapes "^1.0.0" + cli-cursor "^1.0.2" + "log@>= 1.2.0": version "1.4.0" resolved "https://registry.yarnpkg.com/log/-/log-1.4.0.tgz#4ba1d890fde249b031dca03bc37eaaf325656f1c" @@ -5516,6 +5764,10 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" + normalize-path@^2.0.0, normalize-path@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -5535,12 +5787,26 @@ normalize-url@^1.4.0: query-string "^4.1.0" sort-keys "^1.0.0" +npm-path@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.3.tgz#15cff4e1c89a38da77f56f6055b24f975dfb2bbe" + dependencies: + which "^1.2.10" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" dependencies: path-key "^2.0.0" +npm-which@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa" + dependencies: + commander "^2.9.0" + npm-path "^2.0.2" + which "^1.2.10" + "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -5654,6 +5920,10 @@ once@~1.3.0: dependencies: wrappy "1" +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -5686,6 +5956,15 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" +ora@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" + dependencies: + chalk "^1.1.1" + cli-cursor "^1.0.2" + cli-spinners "^0.1.2" + object-assign "^4.0.1" + orchestrator@^0.3.0: version "0.3.8" resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" @@ -5757,6 +6036,10 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-map@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" + pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" @@ -5794,6 +6077,12 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-json@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-3.0.0.tgz#fa6f47b18e23826ead32f263e744d0e1e847fb13" + dependencies: + error-ex "^1.3.1" + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -6210,6 +6499,13 @@ prettier@1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.8.2.tgz#bff83e7fd573933c607875e5ba3abbdffb96aeb8" +pretty-format@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36" + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" + pretty-hrtime@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -6970,6 +7266,10 @@ require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" +require-from-string@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff" + require-hacker@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/require-hacker/-/require-hacker-3.0.1.tgz#0879be067fcf067530665bcce4c89ac81a870477" @@ -7005,6 +7305,13 @@ resolve@^1.0.0, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.2.0, resolve@^1.3.3: dependencies: path-parse "^1.0.5" +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -7058,6 +7365,12 @@ rx-lite@*, rx-lite@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" +rxjs@^5.4.2: + version "5.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.4.tgz#66a466acb21270b5f441645a1141f95fcae10ee6" + dependencies: + symbol-observable "1.0.1" + safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -7313,6 +7626,10 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + slice-ansi@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" @@ -7416,6 +7733,10 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" +staged-git-files@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-0.0.4.tgz#d797e1b551ca7a639dec0237dc6eb4bb9be17d35" + statuses@1, "statuses@>= 1.3.1 < 2", statuses@^1.2.0, statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" @@ -7451,6 +7772,12 @@ stream-http@^2.3.1: to-arraybuffer "^1.0.0" xtend "^4.0.0" +stream-to-observable@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.2.0.tgz#59d6ea393d87c2c0ddac10aa0d561bc6ba6f0e10" + dependencies: + any-observable "^0.2.0" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -7484,6 +7811,14 @@ string_decoder@~1.0.3: dependencies: safe-buffer "~5.1.0" +stringify-object@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.2.1.tgz#2720c2eff940854c819f6ee252aaeb581f30624d" + dependencies: + get-own-enumerable-property-symbols "^2.0.1" + is-obj "^1.0.1" + is-regexp "^1.0.0" + stringstream@~0.0.4, stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -7527,6 +7862,10 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -7611,6 +7950,14 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" +symbol-observable@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" + +symbol-observable@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40" + symbol-observable@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" @@ -8271,6 +8618,12 @@ which@1, which@^1.2.12, which@^1.2.9: dependencies: isexe "^2.0.0" +which@^1.2.10: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" -- GitLab From 6dfeb5fee16bcad055a970858d9e1f2c2b392619 Mon Sep 17 00:00:00 2001 From: Benjamin Chodoroff Date: Wed, 6 Dec 2017 16:09:56 -0500 Subject: [PATCH 120/399] yarn run fmt --- src/app/Main.js | 154 +- src/app/ResolveRoute.js | 71 +- src/app/RootRoute.js | 21 +- src/app/Translator.js | 45 +- src/app/assets/images/favicons/manifest.json | 30 +- src/app/assets/static/manifest.json | 12 +- src/app/client_config.js | 13 +- src/app/components/App.jsx | 500 ++++-- src/app/components/cards/CardView.js | 103 +- src/app/components/cards/CategorySelector.jsx | 146 +- src/app/components/cards/Comment.jsx | 577 ++++--- src/app/components/cards/MarkdownViewer.jsx | 207 ++- src/app/components/cards/PostFull.jsx | 609 ++++--- src/app/components/cards/PostHistoryRow.jsx | 52 +- src/app/components/cards/PostSummary.jsx | 340 ++-- src/app/components/cards/PostsList.jsx | 173 +- .../components/cards/TransferHistoryRow.jsx | 259 ++- src/app/components/cards/UserListRow.jsx | 42 +- src/app/components/cards/VoteHistoryRow.jsx | 55 +- src/app/components/elements/Author.jsx | 98 +- .../components/elements/AuthorDropdown.jsx | 45 +- src/app/components/elements/Callout.jsx | 18 +- .../components/elements/ChangePassword.jsx | 481 ++++-- .../components/elements/CheckLoginOwner.jsx | 167 +- .../components/elements/ConvertToSteem.jsx | 183 +- src/app/components/elements/CountryCode.jsx | 528 +++--- .../components/elements/DateJoinWrapper.jsx | 9 +- src/app/components/elements/DepthChart.jsx | 176 +- src/app/components/elements/DropdownMenu.jsx | 85 +- src/app/components/elements/Follow.jsx | 173 +- .../components/elements/FormattedAsset.jsx | 25 +- .../elements/FoundationDropdown.jsx | 40 +- .../elements/FoundationDropdownMenu.jsx | 43 +- .../elements/GeneratedPasswordInput.jsx | 89 +- src/app/components/elements/HelpContent.jsx | 109 +- .../components/elements/HorizontalMenu.jsx | 46 +- src/app/components/elements/Icon.jsx | 47 +- src/app/components/elements/KeyEdit.js | 195 ++- src/app/components/elements/Keys.jsx | 101 +- src/app/components/elements/Link.js | 32 +- .../components/elements/LoadingIndicator.jsx | 52 +- .../elements/MarkNotificationRead.jsx | 16 +- src/app/components/elements/Memo.js | 49 +- src/app/components/elements/NotifiCounter.jsx | 20 +- src/app/components/elements/OrderHistory.jsx | 116 +- src/app/components/elements/Orderbook.jsx | 176 +- src/app/components/elements/OrderbookRow.jsx | 35 +- .../components/elements/OrderhistoryRow.jsx | 21 +- .../components/elements/PageViewsCounter.jsx | 33 +- src/app/components/elements/PasswordInput.jsx | 105 +- src/app/components/elements/PasswordReset.jsx | 48 +- src/app/components/elements/QrKeyView.jsx | 18 +- src/app/components/elements/QrReader.jsx | 26 +- src/app/components/elements/Reblog.jsx | 148 +- src/app/components/elements/ReplyEditor.jsx | 1219 +++++++++----- src/app/components/elements/Reputation.jsx | 12 +- .../elements/SavingsWithdrawHistory.jsx | 170 +- src/app/components/elements/ShareMenu.jsx | 53 +- src/app/components/elements/ShowKey.js | 155 +- src/app/components/elements/SidebarLinks.jsx | 24 +- .../components/elements/SidebarNewUsers.jsx | 41 +- src/app/components/elements/SidebarStats.jsx | 4 +- src/app/components/elements/SlateEditor.jsx | 642 +++---- .../components/elements/SuggestPassword.jsx | 139 +- src/app/components/elements/SvgImage.jsx | 20 +- src/app/components/elements/TagList.jsx | 61 +- src/app/components/elements/TimeAgoWrapper.js | 21 +- src/app/components/elements/Tooltip.jsx | 12 +- .../components/elements/TransactionError.jsx | 79 +- src/app/components/elements/UserKeys.jsx | 234 ++- src/app/components/elements/UserList.jsx | 110 +- src/app/components/elements/UserNames.jsx | 53 +- src/app/components/elements/Userpic.jsx | 47 +- src/app/components/elements/VerticalMenu.jsx | 57 +- .../components/elements/VotesAndComments.jsx | 68 +- src/app/components/elements/Voting.jsx | 525 ++++-- src/app/components/elements/WalletSubMenu.jsx | 49 +- .../components/elements/YoutubePreview.jsx | 63 +- .../components/modules/AddToWaitingList.jsx | 84 +- .../modules/ArticleLayoutSelector.jsx | 47 +- src/app/components/modules/AuthorRewards.jsx | 274 +-- src/app/components/modules/BottomPanel.jsx | 16 +- .../modules/ConfirmTransactionForm.jsx | 154 +- .../components/modules/CurationRewards.jsx | 234 ++- src/app/components/modules/Dialogs.jsx | 178 +- src/app/components/modules/ExplorePost.jsx | 85 +- src/app/components/modules/Footer.jsx | 33 +- src/app/components/modules/Header.jsx | 254 ++- src/app/components/modules/Header.test.js | 2 +- src/app/components/modules/LoginForm.jsx | 505 ++++-- src/app/components/modules/MiniHeader.jsx | 61 +- src/app/components/modules/Modals.jsx | 97 +- src/app/components/modules/Powerdown.jsx | 238 ++- src/app/components/modules/PromotePost.jsx | 186 ++- src/app/components/modules/Settings.jsx | 486 ++++-- src/app/components/modules/SidePanel.jsx | 25 +- src/app/components/modules/SidebarModule.jsx | 34 +- src/app/components/modules/TermsAgree.jsx | 1481 +++++++++++++++-- src/app/components/modules/TopRightMenu.jsx | 200 ++- src/app/components/modules/Transfer.jsx | 574 +++++-- src/app/components/modules/UserWallet.jsx | 924 +++++++--- src/app/components/modules/lp/LpFooter.jsx | 9 +- src/app/components/modules/lp/LpHeader.jsx | 9 +- src/app/components/pages/About.jsx | 30 +- src/app/components/pages/Approval.jsx | 76 +- .../components/pages/ChangePasswordPage.jsx | 14 +- src/app/components/pages/CreateAccount.jsx | 430 +++-- src/app/components/pages/Faq.jsx | 4 +- src/app/components/pages/Index.jsx | 12 +- src/app/components/pages/Login.jsx | 13 +- src/app/components/pages/Market.jsx | 1133 +++++++++---- src/app/components/pages/NotFound.jsx | 44 +- src/app/components/pages/PickAccount.jsx | 294 +++- src/app/components/pages/Post.jsx | 244 ++- src/app/components/pages/PostPage.jsx | 2 +- .../components/pages/PostPageNoCategory.jsx | 69 +- src/app/components/pages/PostsIndex.jsx | 190 ++- src/app/components/pages/Privacy.jsx | 431 +++-- .../components/pages/RecoverAccountStep1.jsx | 433 +++-- .../components/pages/RecoverAccountStep2.jsx | 219 ++- src/app/components/pages/SubmitPost.jsx | 19 +- .../pages/SubmitPostServerRender.jsx | 8 +- src/app/components/pages/Support.jsx | 9 +- src/app/components/pages/TagsIndex.jsx | 125 +- src/app/components/pages/Topics.jsx | 68 +- src/app/components/pages/Tos.jsx | 1238 ++++++++++++-- src/app/components/pages/UserProfile.jsx | 749 ++++++--- src/app/components/pages/WaitingList.jsx | 19 +- src/app/components/pages/Welcome.jsx | 6 +- src/app/components/pages/Witnesses.jsx | 475 ++++-- src/app/components/pages/XSS.jsx | 76 +- src/app/locales/counterpart/es.js | 24 +- src/app/locales/counterpart/fr.js | 24 +- src/app/locales/counterpart/it.js | 24 +- src/app/locales/counterpart/ko.js | 24 +- src/app/locales/en.json | 1423 ++++++++-------- src/app/locales/es.json | 1422 +++++++++------- src/app/locales/fr.json | 1437 +++++++++------- src/app/locales/it.json | 1426 +++++++++------- src/app/locales/ko.json | 1420 ++++++++-------- src/app/locales/ru.json | 1462 ++++++++-------- src/app/redux/AppReducer.js | 36 +- src/app/redux/AuthSaga.js | 193 ++- src/app/redux/DemoState.js | 151 +- src/app/redux/EmptyState.js | 10 +- src/app/redux/FetchDataSaga.js | 357 ++-- src/app/redux/FetchDataSaga.test.js | 10 +- src/app/redux/FollowSaga.js | 146 +- src/app/redux/GlobalReducer.js | 343 ++-- src/app/redux/MarketReducer.js | 16 +- src/app/redux/MarketSaga.js | 62 +- src/app/redux/OffchainReducer.js | 4 +- src/app/redux/PollDataSaga.js | 22 +- src/app/redux/RootReducer.js | 32 +- src/app/redux/SagaShared.js | 65 +- src/app/redux/TransactionReducer.js | 101 +- src/app/redux/TransactionSaga.js | 980 +++++++---- src/app/redux/UserReducer.js | 151 +- src/app/redux/UserSaga.js | 619 ++++--- src/app/redux/tests/AppReducer.test.js | 21 +- src/app/redux/tests/global.json | 158 +- src/app/redux/tests/global.test.js | 14 +- src/app/utils/Accessors.js | 14 +- src/app/utils/AppPropTypes.js | 6 +- src/app/utils/BadActorList.js | 4 +- src/app/utils/BrowserTests.js | 60 +- src/app/utils/ChainValidation.js | 29 +- src/app/utils/ComponentFormatters.jsx | 14 +- src/app/utils/ConsoleExports.js | 72 +- src/app/utils/ContentPreview.js | 2 +- src/app/utils/DMCAList.js | 4 +- src/app/utils/DMCAUserList.js | 4 +- src/app/utils/DomUtils.js | 7 +- src/app/utils/ExtractContent.js | 94 +- src/app/utils/ExtractMeta.js | 133 +- src/app/utils/FormatCoins.js | 30 +- src/app/utils/FormatDecimal.test.js | 6 +- src/app/utils/Html.js | 14 +- src/app/utils/ImageUserBlockList.js | 4 +- src/app/utils/JsPlugins.js | 43 +- src/app/utils/Links.js | 80 +- src/app/utils/Links.test.js | 350 ++-- src/app/utils/MarketClasses.js | 58 +- src/app/utils/MarketUtils.js | 16 +- src/app/utils/NormalizeProfile.js | 65 +- src/app/utils/Notifications.js | 20 +- src/app/utils/ParsersAndFormatters.js | 82 +- src/app/utils/ProxifyUrl.js | 7 +- src/app/utils/ProxifyUrl.test.js | 166 +- src/app/utils/ReactForm.js | 241 +-- src/app/utils/ReduxForms.js | 32 +- src/app/utils/RegisterServiceWorker.js | 59 +- src/app/utils/RemarkablePlugin.js | 6 +- src/app/utils/RemarkableStripper.js | 20 +- src/app/utils/SanitizeConfig.js | 176 +- src/app/utils/ServerApiClient.js | 102 +- src/app/utils/SlateEditor/Align.js | 30 +- src/app/utils/SlateEditor/DemoState.js | 22 +- src/app/utils/SlateEditor/HRule.js | 10 +- src/app/utils/SlateEditor/Helpers.js | 14 +- src/app/utils/SlateEditor/Iframe.js | 121 +- src/app/utils/SlateEditor/Image.js | 221 +-- src/app/utils/SlateEditor/Link.js | 19 +- src/app/utils/SlateEditor/Schema.js | 463 ++++-- src/app/utils/StateFunctions.js | 176 +- src/app/utils/UserUtil.js | 5 +- src/app/utils/VerifiedExchangeList.js | 4 +- src/app/utils/shouldComponentUpdate.js | 71 +- src/app/utils/userIllegalContent.js | 4 +- src/db/config/config.json | 14 +- .../migrations/20160419161331-create-user.js | 138 +- .../20160420133848-create-identity.js | 114 +- .../20160420151336-create-account.js | 128 +- .../20160506223257-create-web-events.js | 86 +- .../20160519211043-users-waiting-list.js | 6 +- ...20160715233035-account-recovery-request.js | 98 +- .../migrations/20160930210310-create-list.js | 56 +- .../migrations/20161129170500-create-page.js | 46 +- .../migrations/20170426204791-wait-columns.js | 33 +- ...20170511160822-not_unique_account_names.js | 11 +- .../20170518201152-create-attributes.js | 66 +- src/db/models/account.js | 72 +- src/db/models/account_recovery_request.js | 81 +- src/db/models/identity.js | 68 +- src/db/models/index.js | 69 +- src/db/models/list.js | 26 +- src/db/models/page.js | 26 +- src/db/models/user.js | 74 +- src/db/models/user_attributes.js | 56 +- src/db/models/web_event.js | 68 +- src/db/models/web_events.js | 28 +- src/db/tarantool.js | 30 +- src/db/utils/find_user.js | 14 +- src/server/api/account_recovery.js | 253 ++- src/server/api/general.js | 594 +++++-- src/server/api/notifications.js | 108 +- src/server/api/oauth.js | 242 ++- src/server/app_render.jsx | 46 +- src/server/hardwarestats.js | 55 +- src/server/index.js | 32 +- src/server/json/post_json.jsx | 20 +- src/server/json/user_json.jsx | 24 +- src/server/prod_logger.js | 49 +- src/server/record_web_event.js | 17 +- src/server/redirects.js | 13 +- src/server/requesttimings.js | 13 +- src/server/sendEmail.js | 37 +- src/server/server-error.jsx | 21 +- src/server/server-html.jsx | 224 ++- src/server/server.js | 79 +- src/server/server.test.js | 6 +- src/server/service-worker.js | 42 +- .../sign_up_pages/enter_confirm_email.jsx | 405 +++-- .../sign_up_pages/enter_confirm_mobile.jsx | 212 ++- src/server/utils/misc.js | 28 +- src/server/utils/teleSign.js | 50 +- src/server/utils/twilio.js | 107 +- src/shared/HtmlReady.js | 380 +++-- src/shared/HtmlReady.test.js | 48 +- src/shared/UniversalRender.jsx | 285 ++-- src/shared/api_client/ChainConfig.js | 14 +- src/shared/api_client/index.js | 6 +- src/shared/clash/object2json.js | 54 +- 263 files changed, 27759 insertions(+), 15910 deletions(-) diff --git a/src/app/Main.js b/src/app/Main.js index 2594519c0..6f57d38ce 100644 --- a/src/app/Main.js +++ b/src/app/Main.js @@ -1,15 +1,15 @@ import 'babel-core/register'; import 'babel-polyfill'; import 'whatwg-fetch'; -import {VIEW_MODE_WHISTLE, PARAM_VIEW_MODE} from 'shared/constants'; +import { VIEW_MODE_WHISTLE, PARAM_VIEW_MODE } from 'shared/constants'; import './assets/stylesheets/app.scss'; import plugins from 'app/utils/JsPlugins'; import Iso from 'iso'; import universalRender from 'shared/UniversalRender'; import ConsoleExports from './utils/ConsoleExports'; -import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; +import { serverApiRecordEvent } from 'app/utils/ServerApiClient'; import * as steem from '@steemit/steem-js'; -import {determineViewMode} from "app/utils/Links"; +import { determineViewMode } from 'app/utils/Links'; window.onerror = error => { if (window.$STM_csrf) serverApiRecordEvent('client_error', error); @@ -20,86 +20,92 @@ const CMD_LOG_TOGGLE = 'log-toggle'; const CMD_LOG_O = 'log-on'; try { - if(process.env.NODE_ENV === 'development') { + if (process.env.NODE_ENV === 'development') { // Adds some object refs to the global window object - ConsoleExports.init(window) + ConsoleExports.init(window); } } catch (e) { - console.error(e) + console.error(e); } -let offchain // loaded by main() +let offchain; // loaded by main() // iso default selector that injects offchain state const isoSelector = () => { - const all = document.querySelectorAll('[data-iso-key]') - return Array.prototype.reduce.call(all, (cache, node) => { - const key = node.getAttribute('data-iso-key') - if (!cache[key]) cache[key] = {} - if (node.nodeName === 'SCRIPT') { - try { - const state = JSON.parse(node.innerHTML) - state.offchain = offchain - cache[key].state = state - } catch (e) { - cache[key].state = {} - } - } else { - cache[key].node = node - } - return cache - }, {}) -} + const all = document.querySelectorAll('[data-iso-key]'); + return Array.prototype.reduce.call( + all, + (cache, node) => { + const key = node.getAttribute('data-iso-key'); + if (!cache[key]) cache[key] = {}; + if (node.nodeName === 'SCRIPT') { + try { + const state = JSON.parse(node.innerHTML); + state.offchain = offchain; + cache[key].state = state; + } catch (e) { + cache[key].state = {}; + } + } else { + cache[key].node = node; + } + return cache; + }, + {} + ); +}; async function runApp(initial_state) { console.log('Initial state', initial_state); const konami = { code: 'xyzzy', - enabled: false + enabled: false, }; const buff = konami.code.split(''); - const cmd = (command) => { + const cmd = command => { console.log('got command:' + command); switch (command) { - case CMD_LOG_O : + case CMD_LOG_O: konami.enabled = false; - case CMD_LOG_TOGGLE : - case CMD_LOG_T : + case CMD_LOG_TOGGLE: + case CMD_LOG_T: konami.enabled = !konami.enabled; - if(konami.enabled) { - steem.api.setOptions({logger: console}); + if (konami.enabled) { + steem.api.setOptions({ logger: console }); } else { - steem.api.setOptions({logger: false}); + steem.api.setOptions({ logger: false }); } return 'api logging ' + konami.enabled; - default : + default: return 'That command is not supported.'; } //return 'done'; - } + }; const enableKonami = () => { - if(!window.s) { + if (!window.s) { console.log('The cupie doll is yours.'); - window.s = (command) => { return cmd.call(this, command) }; + window.s = command => { + return cmd.call(this, command); + }; } - } + }; - window.document.body.onkeypress = (e) => { - buff.shift() - buff.push(e.key) - if(buff.join('') === konami.code) { + window.document.body.onkeypress = e => { + buff.shift(); + buff.push(e.key); + if (buff.join('') === konami.code) { enableKonami(); - cmd(CMD_LOG_T) + cmd(CMD_LOG_T); } }; - if(window.location.hash.indexOf('#'+konami.code) === 0) { - enableKonami() - cmd(CMD_LOG_O) + if (window.location.hash.indexOf('#' + konami.code) === 0) { + enableKonami(); + cmd(CMD_LOG_O); } - const config = initial_state.offchain.config + const config = initial_state.offchain.config; steem.api.setOptions({ url: config.steemd_connection_client }); steem.config.set('address_prefix', config.address_prefix); steem.config.set('chain_id', config.chain_id); @@ -115,41 +121,47 @@ async function runApp(initial_state) { initial_state.app.viewMode = determineViewMode(window.location.search); - const location = `${window.location.pathname}${window.location.search}${window.location.hash}`; - universalRender({history, location, initial_state}) - .catch(error => { + const location = `${window.location.pathname}${window.location.search}${ + window.location.hash + }`; + universalRender({ history, location, initial_state }).catch(error => { console.error(error); serverApiRecordEvent('client_error', error); }); } async function getOffchainState() { - const state = await (await fetch('/api/v1/state', {credentials: 'same-origin'})).json() - const now = Date.now() / 1000 - const lastVisit = localStorage.getItem('lastVisit') || -Infinity - const loginData = localStorage.getItem('autopost2') - state.new_visit = (loginData == null && now - lastVisit > 1800) - return state + const state = await (await fetch('/api/v1/state', { + credentials: 'same-origin', + })).json(); + const now = Date.now() / 1000; + const lastVisit = localStorage.getItem('lastVisit') || -Infinity; + const loginData = localStorage.getItem('autopost2'); + state.new_visit = loginData == null && now - lastVisit > 1800; + return state; } async function main() { - offchain = await getOffchainState() - localStorage.setItem('lastVisit', Date.now() / 1000) + offchain = await getOffchainState(); + localStorage.setItem('lastVisit', Date.now() / 1000); if (!window.Intl) { - require.ensure(['intl/dist/Intl'], (require) => { - window.IntlPolyfill = window.Intl = require('intl/dist/Intl') - require('intl/locale-data/jsonp/en-US.js') - require('intl/locale-data/jsonp/ru.js'); - require('intl/locale-data/jsonp/fr.js'); - require('intl/locale-data/jsonp/it.js'); - require('intl/locale-data/jsonp/ko.js'); - require('intl/locale-data/jsonp/es.js') - Iso.bootstrap(runApp, isoSelector); - }, "IntlBundle"); - } - else { + require.ensure( + ['intl/dist/Intl'], + require => { + window.IntlPolyfill = window.Intl = require('intl/dist/Intl'); + require('intl/locale-data/jsonp/en-US.js'); + require('intl/locale-data/jsonp/ru.js'); + require('intl/locale-data/jsonp/fr.js'); + require('intl/locale-data/jsonp/it.js'); + require('intl/locale-data/jsonp/ko.js'); + require('intl/locale-data/jsonp/es.js'); + Iso.bootstrap(runApp, isoSelector); + }, + 'IntlBundle' + ); + } else { Iso.bootstrap(runApp, isoSelector); } } -main() +main(); diff --git a/src/app/ResolveRoute.js b/src/app/ResolveRoute.js index 016896200..41a99d06f 100644 --- a/src/app/ResolveRoute.js +++ b/src/app/ResolveRoute.js @@ -4,7 +4,7 @@ export const routeRegex = { UserProfile2: /^\/(@[\w\.\d-]+)\/(blog|posts|comments|recommended|transfers|curation-rewards|author-rewards|permissions|created|recent-replies|feed|password|followed|followers|settings)\/?$/, UserProfile3: /^\/(@[\w\.\d-]+)\/[\w\.\d-]+/, UserEndPoints: /^(blog|posts|comments|recommended|transfers|curation-rewards|author-rewards|permissions|created|recent-replies|feed|password|followed|followers|settings)$/, - CategoryFilters: /^\/(hot|votes|responses|trending|trending30|promoted|cashout|payout|payout_comments|created|active)\/?$/ig, + CategoryFilters: /^\/(hot|votes|responses|trending|trending30|promoted|cashout|payout|payout_comments|created|active)\/?$/gi, PostNoCategory: /^\/(@[\w\.\d-]+)\/([\w\d-]+)/, Post: /^\/([\w\d\-\/]+)\/(\@[\w\d\.-]+)\/([\w\d-]+)\/?($|\?)/, PostJson: /^\/([\w\d\-\/]+)\/(\@[\w\d\.-]+)\/([\w\d-]+)(\.json)$/, @@ -12,90 +12,95 @@ export const routeRegex = { UserNameJson: /^.*(?=(\.json))/, }; -export default function resolveRoute(path) -{ +export default function resolveRoute(path) { if (path === '/') { - return {page: 'PostsIndex', params: ['trending']}; + return { page: 'PostsIndex', params: ['trending'] }; } if (path === '/about.html') { - return {page: 'About'}; + return { page: 'About' }; } if (path === '/welcome') { - return {page: 'Welcome'}; + return { page: 'Welcome' }; } if (path === '/faq.html') { - return {page: 'Faq'}; + return { page: 'Faq' }; } if (path === '/login.html') { - return {page: 'Login'}; + return { page: 'Login' }; } if (path === '/privacy.html') { - return {page: 'Privacy'}; + return { page: 'Privacy' }; } if (path === '/support.html') { - return {page: 'Support'}; + return { page: 'Support' }; } if (path === '/xss/test' && process.env.NODE_ENV === 'development') { - return {page: 'XSSTest'}; + return { page: 'XSSTest' }; } if (path.match(/^\/tags\/?/)) { - return {page: 'Tags'}; + return { page: 'Tags' }; } if (path === '/tos.html') { - return {page: 'Tos'}; + return { page: 'Tos' }; } if (path === '/change_password') { - return {page: 'ChangePassword'}; + return { page: 'ChangePassword' }; } if (path === '/create_account') { - return {page: 'CreateAccount'}; + return { page: 'CreateAccount' }; } if (path === '/approval') { - return {page: 'Approval'}; + return { page: 'Approval' }; } if (path === '/pick_account') { - return {page: 'PickAccount'}; + return { page: 'PickAccount' }; } if (path === '/recover_account_step_1') { - return {page: 'RecoverAccountStep1'}; + return { page: 'RecoverAccountStep1' }; } if (path === '/recover_account_step_2') { - return {page: 'RecoverAccountStep2'}; + return { page: 'RecoverAccountStep2' }; } if (path === '/waiting_list.html') { - return {page: 'WaitingList'}; + return { page: 'WaitingList' }; } if (path === '/market') { - return {page: 'Market'}; + return { page: 'Market' }; } if (path === '/~witnesses') { - return {page: 'Witnesses'}; + return { page: 'Witnesses' }; } if (path === '/submit.html') { - return {page: 'SubmitPost'}; + return { page: 'SubmitPost' }; } let match = path.match(routeRegex.PostsIndex); if (match) { - return {page: 'PostsIndex', params: ['home', match[1]]}; + return { page: 'PostsIndex', params: ['home', match[1]] }; } - match = path.match(routeRegex.UserProfile1) || + match = + path.match(routeRegex.UserProfile1) || // @user/"posts" is deprecated in favor of "comments" as of oct-2016 (#443) path.match(routeRegex.UserProfile2); if (match) { - return {page: 'UserProfile', params: match.slice(1)}; + return { page: 'UserProfile', params: match.slice(1) }; } match = path.match(routeRegex.PostNoCategory); if (match) { - return {page: 'PostNoCategory', params: match.slice(1)}; + return { page: 'PostNoCategory', params: match.slice(1) }; } match = path.match(routeRegex.Post); if (match) { - return {page: 'Post', params: match.slice(1)}; - } - match = path.match(/^\/(hot|votes|responses|trending|trending30|promoted|cashout|payout|payout_comments|created|active)\/?$/) - || path.match(/^\/(hot|votes|responses|trending|trending30|promoted|cashout|payout|payout_comments|created|active)\/([\w\d-]+)\/?$/); + return { page: 'Post', params: match.slice(1) }; + } + match = + path.match( + /^\/(hot|votes|responses|trending|trending30|promoted|cashout|payout|payout_comments|created|active)\/?$/ + ) || + path.match( + /^\/(hot|votes|responses|trending|trending30|promoted|cashout|payout|payout_comments|created|active)\/([\w\d-]+)\/?$/ + ); if (match) { - return {page: 'PostsIndex', params: match.slice(1)}; + return { page: 'PostsIndex', params: match.slice(1) }; } - return {page: 'NotFound'}; + return { page: 'NotFound' }; } diff --git a/src/app/RootRoute.js b/src/app/RootRoute.js index 2fc63800b..1ca7845aa 100644 --- a/src/app/RootRoute.js +++ b/src/app/RootRoute.js @@ -34,7 +34,10 @@ export default { //require.ensure([], (require) => { cb(null, [require('app/components/pages/Support')]); //}); - } else if (route.page === 'XSSTest' && process.env.NODE_ENV === 'development') { + } else if ( + route.page === 'XSSTest' && + process.env.NODE_ENV === 'development' + ) { //require.ensure([], (require) => { cb(null, [require('app/components/pages/XSS')]); //}); @@ -81,17 +84,19 @@ export default { } else if (route.page === 'SubmitPost') { if (process.env.BROWSER) { // require.ensure([], (require) => { - cb(null, [require('app/components/pages/SubmitPost')]); + cb(null, [require('app/components/pages/SubmitPost')]); // }); } else { - cb(null, [require('app/components/pages/SubmitPostServerRender')]); + cb(null, [ + require('app/components/pages/SubmitPostServerRender'), + ]); } } else if (route.page === 'UserProfile') { //require.ensure([], (require) => { cb(null, [require('app/components/pages/UserProfile')]); //}); } else if (route.page === 'Market') { - require.ensure([], (require) => { + require.ensure([], require => { cb(null, [require('app/components/pages/Market')]); }); } else if (route.page === 'Post') { @@ -107,11 +112,13 @@ export default { //}); } else { //require.ensure([], (require) => { - cb(process.env.BROWSER ? null : Error(404), [require('app/components/pages/NotFound')]); + cb(process.env.BROWSER ? null : Error(404), [ + require('app/components/pages/NotFound'), + ]); //}); } }, indexRoute: { - component: PostsIndex.component - } + component: PostsIndex.component, + }, }; diff --git a/src/app/Translator.js b/src/app/Translator.js index 7546d3a44..915bb0dda 100644 --- a/src/app/Translator.js +++ b/src/app/Translator.js @@ -1,13 +1,13 @@ import React from 'react'; -import {connect} from 'react-redux' -import {IntlProvider, addLocaleData} from 'react-intl'; +import { connect } from 'react-redux'; +import { IntlProvider, 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 {DEFAULT_LANGUAGE} from 'app/client_config'; +import { DEFAULT_LANGUAGE } from 'app/client_config'; import tt from 'counterpart'; addLocaleData([...en, ...es, ...ru, ...fr, ...it, ...ko]); @@ -31,32 +31,35 @@ tt.registerTranslations('ko', require('app/locales/counterpart/ko')); tt.registerTranslations('ko', require('app/locales/ko.json')); if (process.env.NODE_ENV === 'production') { -tt.setFallbackLocale('en'); + tt.setFallbackLocale('en'); } class Translator extends React.Component { render() { const language = this.props.locale; tt.setLocale(language); - return - {this.props.children} - + return ( + + {this.props.children} + + ); } } -export default connect( - (state, ownProps) => { - const locale = state.app.getIn(['user_preferences', 'locale']); - return {...ownProps, locale}; - } -)(Translator); +export default connect((state, ownProps) => { + const locale = state.app.getIn(['user_preferences', 'locale']); + return { ...ownProps, locale }; +})(Translator); -export const FormattedHTMLMessage = ({id, params, className}) => ( -
    +export const FormattedHTMLMessage = ({ id, params, className }) => ( +
    ); diff --git a/src/app/assets/images/favicons/manifest.json b/src/app/assets/images/favicons/manifest.json index 2d70e259a..65dd72761 100644 --- a/src/app/assets/images/favicons/manifest.json +++ b/src/app/assets/images/favicons/manifest.json @@ -1,17 +1,17 @@ { - "name": "Golos", - "icons": [ - { - "src": "\/images\/favicons\/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image\/png" - }, - { - "src": "\/images\/favicons\/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image\/png" - } - ], - "theme_color": "#ffffff", - "display": "standalone" + "name": "Golos", + "icons": [ + { + "src": "/images/favicons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/images/favicons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "display": "standalone" } diff --git a/src/app/assets/static/manifest.json b/src/app/assets/static/manifest.json index 52cf1fa82..8f7690777 100644 --- a/src/app/assets/static/manifest.json +++ b/src/app/assets/static/manifest.json @@ -1,8 +1,8 @@ { - "name": "Steemit", - "short_name": "Steemit", - "start_url": "/", - "display": "standalone", - "gcm_sender_id": "724829305784", - "gcm_user_visible_only": true + "name": "Steemit", + "short_name": "Steemit", + "start_url": "/", + "display": "standalone", + "gcm_sender_id": "724829305784", + "gcm_user_visible_only": true } diff --git a/src/app/client_config.js b/src/app/client_config.js index bcea2524f..f7fb8989e 100644 --- a/src/app/client_config.js +++ b/src/app/client_config.js @@ -38,13 +38,12 @@ export const FRACTION_DIGITS_MARKET = 3; // accurate amount of deciaml digits (e // meta info export const TWITTER_HANDLE = '@steemit'; -export const SHARE_IMAGE = 'https://' + - APP_DOMAIN + - '/images/steemit-share.png'; -export const TWITTER_SHARE_IMAGE = 'https://' + - APP_DOMAIN + - '/images/steemit-twshare.png'; -export const SITE_DESCRIPTION = 'Steemit is a social media platform where everyone gets paid for ' + +export const SHARE_IMAGE = + 'https://' + APP_DOMAIN + '/images/steemit-share.png'; +export const TWITTER_SHARE_IMAGE = + 'https://' + APP_DOMAIN + '/images/steemit-twshare.png'; +export const SITE_DESCRIPTION = + 'Steemit is a social media platform where everyone gets paid for ' + 'creating and curating content. It leverages a robust digital points system, called Steem, that ' + 'supports real value for digital rewards through market price discovery and liquidity'; diff --git a/src/app/components/App.jsx b/src/app/components/App.jsx index a3352084e..9301894d8 100644 --- a/src/app/components/App.jsx +++ b/src/app/components/App.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import {connect} from 'react-redux'; +import { connect } from 'react-redux'; import AppPropTypes from 'app/utils/AppPropTypes'; import Header from 'app/components/modules/Header'; import LpFooter from 'app/components/modules/lp/LpFooter'; @@ -15,26 +15,34 @@ import Icon from 'app/components/elements/Icon'; import MiniHeader from 'app/components/modules/MiniHeader'; import tt from 'counterpart'; import PageViewsCounter from 'app/components/elements/PageViewsCounter'; -import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; +import { serverApiRecordEvent } from 'app/utils/ServerApiClient'; import { APP_NAME, VESTING_TOKEN, LIQUID_TOKEN } from 'app/client_config'; -import {key_utils} from '@steemit/steem-js/lib/auth/ecc'; +import { key_utils } from '@steemit/steem-js/lib/auth/ecc'; import resolveRoute from 'app/ResolveRoute'; -import {VIEW_MODE_WHISTLE} from 'shared/constants'; +import { VIEW_MODE_WHISTLE } from 'shared/constants'; -const pageRequiresEntropy = (path) => { - const {page} = resolveRoute(path); +const pageRequiresEntropy = path => { + const { page } = resolveRoute(path); const entropyPages = [ - "ChangePassword", "RecoverAccountStep1", "RecoverAccountStep2", - "UserProfile", "CreateAccount" + 'ChangePassword', + 'RecoverAccountStep1', + 'RecoverAccountStep2', + 'UserProfile', + 'CreateAccount', ]; /* Returns true if that page requires the entropy collection listener */ - return entropyPages.indexOf(page) !== -1 -} + return entropyPages.indexOf(page) !== -1; +}; class App extends React.Component { constructor(props) { super(props); - this.state = {open: null, showCallout: true, showBanner: true, expandCallout: false}; + this.state = { + open: null, + showCallout: true, + showBanner: true, + expandCallout: false, + }; this.toggleOffCanvasMenu = this.toggleOffCanvasMenu.bind(this); this.signUp = this.signUp.bind(this); this.learnMore = this.learnMore.bind(this); @@ -44,7 +52,7 @@ class App extends React.Component { } componentWillMount() { - if (process.env.BROWSER) localStorage.removeItem('autopost') // July 14 '16 compromise, renamed to autopost2 + if (process.env.BROWSER) localStorage.removeItem('autopost'); // July 14 '16 compromise, renamed to autopost2 this.props.loginUser(); } @@ -57,23 +65,34 @@ class App extends React.Component { componentWillReceiveProps(np) { /* Add listener if the next page requires entropy and the current page didn't */ - if (pageRequiresEntropy(np.location.pathname) && !pageRequiresEntropy(this.props.location.pathname)) { + if ( + pageRequiresEntropy(np.location.pathname) && + !pageRequiresEntropy(this.props.location.pathname) + ) { this._addEntropyCollector(); - } else if (!pageRequiresEntropy(np.location.pathname)) { // Remove if next page does not require entropy + } else if (!pageRequiresEntropy(np.location.pathname)) { + // Remove if next page does not require entropy this._removeEntropyCollector(); } } _addEntropyCollector() { if (!this.listenerActive && this.refs.App_root) { - this.refs.App_root.addEventListener("mousemove", this.onEntropyEvent, {capture: false, passive: true}); + this.refs.App_root.addEventListener( + 'mousemove', + this.onEntropyEvent, + { capture: false, passive: true } + ); this.listenerActive = true; } } _removeEntropyCollector() { if (this.listenerActive && this.refs.App_root) { - this.refs.App_root.removeEventListener("mousemove", this.onEntropyEvent); + this.refs.App_root.removeEventListener( + 'mousemove', + this.onEntropyEvent + ); this.listenerActive = null; } } @@ -98,10 +117,13 @@ class App extends React.Component { this.refs.side_panel.show(); } - handleClose = () => this.setState({open: null}); + handleClose = () => this.setState({ open: null }); - navigate = (e) => { - const a = e.target.nodeName.toLowerCase() === 'a' ? e.target : e.target.parentNode; + navigate = e => { + const a = + e.target.nodeName.toLowerCase() === 'a' + ? e.target + : e.target.parentNode; // this.setState({open: null}); if (a.host !== window.location.host) return; e.preventDefault(); @@ -109,10 +131,9 @@ class App extends React.Component { }; onEntropyEvent(e) { - if(e.type === 'mousemove') - key_utils.addEntropy(e.pageX, e.pageY, e.screenX, e.screenY) - else - console.log('onEntropyEvent Unknown', e.type, e) + if (e.type === 'mousemove') + key_utils.addEntropy(e.pageX, e.pageY, e.screenX, e.screenY); + else console.log('onEntropyEvent Unknown', e.type, e); } signUp() { @@ -124,53 +145,107 @@ class App extends React.Component { } render() { - const {location, params, children, flash, new_visitor, - depositSteem, username, nightmodeEnabled, viewMode} = this.props; + const { + location, + params, + children, + flash, + new_visitor, + depositSteem, + username, + nightmodeEnabled, + viewMode, + } = this.props; const lp = false; //location.pathname === '/'; - const miniHeader = location.pathname === '/create_account' || location.pathname === '/pick_account'; - const whistleView = (viewMode === VIEW_MODE_WHISTLE); + const miniHeader = + location.pathname === '/create_account' || + location.pathname === '/pick_account'; + const whistleView = viewMode === VIEW_MODE_WHISTLE; const headerHidden = whistleView; const params_keys = Object.keys(params); - const ip = location.pathname === '/' || (params_keys.length === 2 && params_keys[0] === 'order' && params_keys[1] === 'category'); - const alert = this.props.error || flash.get('alert') || flash.get('error'); + const ip = + location.pathname === '/' || + (params_keys.length === 2 && + params_keys[0] === 'order' && + params_keys[1] === 'category'); + const alert = + this.props.error || flash.get('alert') || flash.get('error'); const warning = flash.get('warning'); const success = flash.get('success'); let callout = null; if (this.state.showCallout && (alert || warning || success)) { - callout =
    -
    -
    - this.setState({showCallout: false})} /> -

    {alert || warning || success}

    + callout = ( +
    +
    +
    + + this.setState({ showCallout: false }) + } + /> +

    {alert || warning || success}

    +
    -
    ; - } - else if (false && ip && this.state.showCallout) { - callout =
    -
    -
    - this.setState({showCallout: false})} /> - + ); + } else if (false && ip && this.state.showCallout) { + callout = ( +
    +
    +
    + + this.setState({ showCallout: false }) + } + /> + +
    -
    + ); } if ($STM_Config.read_only_mode && this.state.showCallout) { - callout =
    -
    -
    - this.setState({showCallout: false})} /> -

    {tt('g.read_only_mode')}

    + callout = ( +
    +
    +
    + + this.setState({ showCallout: false }) + } + /> +

    {tt('g.read_only_mode')}

    +
    -
    ; + ); } let welcome_screen = null; @@ -178,12 +253,20 @@ class App extends React.Component { welcome_screen = (
    - this.setState({showBanner: false})} /> + this.setState({ showBanner: false })} + />

    {tt('navigation.intro_tagline')}

    {tt('navigation.intro_paragraph')}


    - {tt('navigation.sign_up')} + + {' '} + {tt('navigation.sign_up')}{' '} + {/* JSX Comment       {tt('navigation.learn_more')} */}
    @@ -194,117 +277,176 @@ class App extends React.Component { const themeClass = nightmodeEnabled ? ' theme-dark' : ' theme-light'; - return
    - - - - - - - {headerHidden ? null : miniHeader ? :
    } -
    - {welcome_screen} - {callout} - {children} - {lp ? : null} + return ( +
    + + + + + + + {headerHidden ? null : miniHeader ? ( + + ) : ( +
    + )} +
    + {welcome_screen} + {callout} + {children} + {lp ? : null} +
    + + +
    - - - -
    + ); } } @@ -314,7 +456,7 @@ App.propTypes = { location: React.PropTypes.object, loginUser: React.PropTypes.func.isRequired, depositSteem: React.PropTypes.func.isRequired, - username: React.PropTypes.string, + username: React.PropTypes.string, }; export default connect( @@ -323,21 +465,29 @@ export default connect( viewMode: state.app.get('viewMode'), error: state.app.get('error'), flash: state.offchain.get('flash'), - new_visitor: !state.user.get('current') && + new_visitor: + !state.user.get('current') && !state.offchain.get('user') && !state.offchain.get('account') && state.offchain.get('new_visit'), - username: state.user.getIn(['current', 'username']) || state.offchain.get('account') || '', - nightmodeEnabled: state.app.getIn(['user_preferences', 'nightmode']), + username: + state.user.getIn(['current', 'username']) || + state.offchain.get('account') || + '', + nightmodeEnabled: state.app.getIn([ + 'user_preferences', + 'nightmode', + ]), }; }, dispatch => ({ - loginUser: () => - dispatch(userActions.usernamePasswordLogin({})), - depositSteem: (username) => { + loginUser: () => dispatch(userActions.usernamePasswordLogin({})), + depositSteem: username => { const new_window = window.open(); new_window.opener = null; - new_window.location = 'https://blocktrades.us/?input_coin_type=btc&output_coin_type=steem&receive_address=' + username; + new_window.location = + 'https://blocktrades.us/?input_coin_type=btc&output_coin_type=steem&receive_address=' + + username; }, }) )(App); diff --git a/src/app/components/cards/CardView.js b/src/app/components/cards/CardView.js index 52ad7d475..6c9951b9e 100644 --- a/src/app/components/cards/CardView.js +++ b/src/app/components/cards/CardView.js @@ -1,8 +1,8 @@ import React from 'react'; -import {connect} from 'react-redux' -import Link from 'app/components/elements/Link' +import { connect } from 'react-redux'; +import Link from 'app/components/elements/Link'; import * as globalActions from 'app/redux/GlobalReducer'; -import links from 'app/utils/Links' +import links from 'app/utils/Links'; import tt from 'counterpart'; /** @deprecated */ @@ -17,57 +17,76 @@ class CardView extends React.Component { // redux clearMetaElement: React.PropTypes.func, - } + }; static defaultProps = { - canEdit: false - } + canEdit: false, + }; constructor() { - super() - this.onCloseImage = (e) => { - e.preventDefault() - const {formId, clearMetaElement} = this.props - clearMetaElement(formId, 'image') - } - this.onCloseDescription = (e) => { - e.preventDefault() - const {formId, clearMetaElement} = this.props - clearMetaElement(formId, 'description') - } + super(); + this.onCloseImage = e => { + e.preventDefault(); + const { formId, clearMetaElement } = this.props; + clearMetaElement(formId, 'image'); + }; + this.onCloseDescription = e => { + e.preventDefault(); + const { formId, clearMetaElement } = this.props; + clearMetaElement(formId, 'description'); + }; } render() { - const {metaLinkData, canEdit} = this.props - if(!metaLinkData) return - const {link, image, description, alt} = metaLinkData + const { metaLinkData, canEdit } = this.props; + if (!metaLinkData) return ; + const { link, image, description, alt } = metaLinkData; // http://postimg.org/image/kbefrpbe9/ - if(!image && !description) return + if (!image && !description) return ; // youTubeImages have their own preview - const youTubeImage = links.youTube.test(link) - return ( - {image && !youTubeImage &&
    - {canEdit && } - - {alt} - -
    } - {description &&
    - {canEdit && ({tt('g.remove')})} - -
    {description}
    - -
    } -
    ) + const youTubeImage = links.youTube.test(link); + return ( + + {image && + !youTubeImage && ( +
    + {canEdit && ( + + )} + + {alt} + +
    + )} + {description && ( +
    + {canEdit && ( + + ( + {tt('g.remove')} + ) + + )} + +
    {description}
    + +
    + )} +
    + ); } } export default connect( (state, ownProps) => { // const {text} = ownProps - const formId = ownProps.formId - const metaLinkData = state.global.getIn(['metaLinkData', formId]) - return {metaLinkData, ...ownProps}; + const formId = ownProps.formId; + const metaLinkData = state.global.getIn(['metaLinkData', formId]); + return { metaLinkData, ...ownProps }; }, dispatch => ({ clearMetaElement: (formId, element) => { - dispatch(globalActions.clearMetaElement({formId, element})) - } + dispatch(globalActions.clearMetaElement({ formId, element })); + }, }) -)(CardView) +)(CardView); diff --git a/src/app/components/cards/CategorySelector.jsx b/src/app/components/cards/CategorySelector.jsx index e8d279ea7..62e2cc700 100644 --- a/src/app/components/cards/CategorySelector.jsx +++ b/src/app/components/cards/CategorySelector.jsx @@ -1,7 +1,7 @@ import React from 'react'; -import {connect} from 'react-redux' -import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' -import {cleanReduxInput} from 'app/utils/ReduxForms' +import { connect } from 'react-redux'; +import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; +import { cleanReduxInput } from 'app/utils/ReduxForms'; import tt from 'counterpart'; class CategorySelector extends React.Component { @@ -19,79 +19,113 @@ class CategorySelector extends React.Component { // redux connect (overwrite in HTML) trending: React.PropTypes.object.isRequired, // Immutable.List - } + }; static defaultProps = { autoComplete: 'on', id: 'CategorySelectorId', isEdit: false, - } + }; constructor() { - super() - this.state = {createCategory: true} - this.shouldComponentUpdate = shouldComponentUpdate(this, 'CategorySelector') - this.categoryCreateToggle = (e) => { - e.preventDefault() - this.props.onChange() - this.setState({ createCategory: !this.state.createCategory }) - setTimeout(() => this.refs.categoryRef.focus(), 300) - } - this.categorySelectOnChange = (e) => { - e.preventDefault() - const {value} = e.target - const {onBlur} = this.props // call onBlur to trigger validation immediately + super(); + this.state = { createCategory: true }; + this.shouldComponentUpdate = shouldComponentUpdate( + this, + 'CategorySelector' + ); + this.categoryCreateToggle = e => { + e.preventDefault(); + this.props.onChange(); + this.setState({ createCategory: !this.state.createCategory }); + setTimeout(() => this.refs.categoryRef.focus(), 300); + }; + this.categorySelectOnChange = e => { + e.preventDefault(); + const { value } = e.target; + const { onBlur } = this.props; // call onBlur to trigger validation immediately if (value === 'new') { - this.setState({createCategory: true}) - setTimeout(() => { if(onBlur) onBlur(); this.refs.categoryRef.focus() }, 300) - } else - this.props.onChange(e) - } + this.setState({ createCategory: true }); + setTimeout(() => { + if (onBlur) onBlur(); + this.refs.categoryRef.focus(); + }, 300); + } else this.props.onChange(e); + }; } render() { - const {trending, tabIndex, disabled} = this.props - const categories = trending.slice(0, 11).filterNot(c => validateCategory(c)) - const {createCategory} = this.state + const { trending, tabIndex, disabled } = this.props; + const categories = trending + .slice(0, 11) + .filterNot(c => validateCategory(c)); + const { createCategory } = this.state; - const categoryOptions = categories.map((c, idx) => - ) + const categoryOptions = categories.map((c, idx) => ( + + )); - const impProps = {...this.props} - const categoryInput = - + const impProps = { ...this.props }; + const categoryInput = ( + + ); const categorySelect = ( - - ) - return ( - - {createCategory ? categoryInput : categorySelect} - - ) + + ); + return {createCategory ? categoryInput : categorySelect}; } } export function validateCategory(category, required = true) { - if(!category || category.trim() === '') return required ? tt('g.required') : null - const cats = category.trim().split(' ') + if (!category || category.trim() === '') + return required ? tt('g.required') : null; + const cats = category.trim().split(' '); return ( // !category || category.trim() === '' ? 'Required' : - cats.length > 5 ? tt('category_selector_jsx.use_limited_amount_of_categories', {amount: 5}) : - cats.find(c => c.length > 24) ? tt('category_selector_jsx.maximum_tag_length_is_24_characters') : - cats.find(c => c.split('-').length > 2) ? tt('category_selector_jsx.use_one_dash') : - cats.find(c => c.indexOf(',') >= 0) ? tt('category_selector_jsx.use_spaces_to_separate_tags') : - cats.find(c => /[A-Z]/.test(c)) ? tt('category_selector_jsx.use_only_lowercase_letters') : - cats.find(c => !/^[a-z0-9-#]+$/.test(c)) ? tt('category_selector_jsx.use_only_allowed_characters') : - cats.find(c => !/^[a-z-#]/.test(c)) ? tt('category_selector_jsx.must_start_with_a_letter') : - cats.find(c => !/[a-z0-9]$/.test(c)) ? tt('category_selector_jsx.must_end_with_a_letter_or_number') : - null - ) + cats.length > 5 + ? tt('category_selector_jsx.use_limited_amount_of_categories', { + amount: 5, + }) + : cats.find(c => c.length > 24) + ? tt('category_selector_jsx.maximum_tag_length_is_24_characters') + : cats.find(c => c.split('-').length > 2) + ? tt('category_selector_jsx.use_one_dash') + : cats.find(c => c.indexOf(',') >= 0) + ? tt('category_selector_jsx.use_spaces_to_separate_tags') + : cats.find(c => /[A-Z]/.test(c)) + ? tt('category_selector_jsx.use_only_lowercase_letters') + : cats.find(c => !/^[a-z0-9-#]+$/.test(c)) + ? tt('category_selector_jsx.use_only_allowed_characters') + : cats.find(c => !/^[a-z-#]/.test(c)) + ? tt('category_selector_jsx.must_start_with_a_letter') + : cats.find(c => !/[a-z0-9]$/.test(c)) + ? tt( + 'category_selector_jsx.must_end_with_a_letter_or_number' + ) + : null + ); } export default connect((state, ownProps) => { - const trending = state.global.getIn(['tag_idx', 'trending']) + const trending = state.global.getIn(['tag_idx', 'trending']); // apply translations // they are used here because default prop can't acces intl property const placeholder = tt('category_selector_jsx.tag_your_story'); - return { trending, placeholder, ...ownProps, } + return { trending, placeholder, ...ownProps }; })(CategorySelector); diff --git a/src/app/components/cards/Comment.jsx b/src/app/components/cards/Comment.jsx index 8ee2e780c..aa9066c90 100644 --- a/src/app/components/cards/Comment.jsx +++ b/src/app/components/cards/Comment.jsx @@ -2,7 +2,7 @@ import React from 'react'; import Author from 'app/components/elements/Author'; import ReplyEditor from 'app/components/elements/ReplyEditor'; import MarkdownViewer from 'app/components/cards/MarkdownViewer'; -import shouldComponentUpdate from 'app/utils/shouldComponentUpdate' +import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; import Voting from 'app/components/elements/Voting'; import { connect } from 'react-redux'; import { Link } from 'react-router'; @@ -11,80 +11,82 @@ import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper'; import Userpic from 'app/components/elements/Userpic'; import * as transactionActions from 'app/redux/TransactionReducer'; import tt from 'counterpart'; -import {parsePayoutAmount} from 'app/utils/ParsersAndFormatters'; -import {Long} from 'bytebuffer'; +import { parsePayoutAmount } from 'app/utils/ParsersAndFormatters'; +import { Long } from 'bytebuffer'; import ImageUserBlockList from 'app/utils/ImageUserBlockList'; // returns true if the comment has a 'hide' flag AND has no descendants w/ positive payout function hideSubtree(cont, c) { - return cont.getIn([c, 'stats', 'hide']) && !hasPositivePayout(cont, c) + return cont.getIn([c, 'stats', 'hide']) && !hasPositivePayout(cont, c); } function hasPositivePayout(cont, c) { - const post = cont.get(c) - if(post.getIn(['stats', 'hasPendingPayout'])) { + const post = cont.get(c); + if (post.getIn(['stats', 'hasPendingPayout'])) { return true; } - if(post.get('replies').find(reply => hasPositivePayout(cont, reply))) { + if (post.get('replies').find(reply => hasPositivePayout(cont, reply))) { return true; } return false; } +export function sortComments(cont, comments, sort_order) { + function netNegative(a) { + return a.get('net_rshares') < 0; + } + function totalPayout(a) { + return ( + parsePayoutAmount(a.get('pending_payout_value')) + + parsePayoutAmount(a.get('total_payout_value')) + + parsePayoutAmount(a.get('curator_payout_value')) + ); + } + function netRshares(a) { + return Long.fromString(String(a.get('net_rshares'))); + } + function countUpvotes(a) { + return a.get('active_votes').filter(vote => vote.get('percent') > 0) + .size; + } -export function sortComments( cont, comments, sort_order ) { - function netNegative(a) { - return a.get("net_rshares") < 0; - } - function totalPayout(a) { - return parsePayoutAmount(a.get('pending_payout_value')) - + parsePayoutAmount(a.get('total_payout_value')) - + parsePayoutAmount(a.get('curator_payout_value')); - } - function netRshares(a) { - return Long.fromString(String(a.get('net_rshares'))) - } - function countUpvotes(a) { - return a.get('active_votes').filter(vote => vote.get('percent') > 0).size - } - - /** sorts replies by upvotes, age, or payout */ - const sort_orders = { - votes: (a, b) => { - const aactive = countUpvotes(cont.get(a)) - const bactive = countUpvotes(cont.get(b)) - return bactive - aactive; - }, - new: (a, b) => { - const acontent = cont.get(a); - const bcontent = cont.get(b); - if (netNegative(acontent)) { - return 1; - } else if (netNegative(bcontent)) { - return -1; - } - const aactive = Date.parse( acontent.get('created') ); - const bactive = Date.parse( bcontent.get('created') ); - return bactive - aactive; - }, - trending: (a, b) => { - const acontent = cont.get(a); - const bcontent = cont.get(b); - if (netNegative(acontent)) { - return 1; - } else if (netNegative(bcontent)) { - return -1; - } - const apayout = totalPayout(acontent) - const bpayout = totalPayout(bcontent) - if(apayout !== bpayout) { - return bpayout - apayout; - } - // If SBD payouts were equal, fall back to rshares sorting - return netRshares(bcontent).compare(netRshares(acontent)) - } - } - comments.sort( sort_orders[sort_order] ); + /** sorts replies by upvotes, age, or payout */ + const sort_orders = { + votes: (a, b) => { + const aactive = countUpvotes(cont.get(a)); + const bactive = countUpvotes(cont.get(b)); + return bactive - aactive; + }, + new: (a, b) => { + const acontent = cont.get(a); + const bcontent = cont.get(b); + if (netNegative(acontent)) { + return 1; + } else if (netNegative(bcontent)) { + return -1; + } + const aactive = Date.parse(acontent.get('created')); + const bactive = Date.parse(bcontent.get('created')); + return bactive - aactive; + }, + trending: (a, b) => { + const acontent = cont.get(a); + const bcontent = cont.get(b); + if (netNegative(acontent)) { + return 1; + } else if (netNegative(bcontent)) { + return -1; + } + const apayout = totalPayout(acontent); + const bpayout = totalPayout(bcontent); + if (apayout !== bpayout) { + return bpayout - apayout; + } + // If SBD payouts were equal, fall back to rshares sorting + return netRshares(bcontent).compare(netRshares(acontent)); + }, + }; + comments.sort(sort_orders[sort_order]); } class CommentImpl extends React.Component { @@ -92,7 +94,8 @@ class CommentImpl extends React.Component { // html props cont: React.PropTypes.object.isRequired, content: React.PropTypes.string.isRequired, - sort_order: React.PropTypes.oneOf(['votes', 'new', 'trending']).isRequired, + sort_order: React.PropTypes.oneOf(['votes', 'new', 'trending']) + .isRequired, root: React.PropTypes.bool, showNegativeComments: React.PropTypes.bool, onHide: React.PropTypes.func, @@ -109,54 +112,62 @@ class CommentImpl extends React.Component { }; static defaultProps = { depth: 1, - } + }; constructor() { super(); - this.state = {collapsed: false, hide_body: false, highlight: false}; + this.state = { collapsed: false, hide_body: false, highlight: false }; this.revealBody = this.revealBody.bind(this); - this.shouldComponentUpdate = shouldComponentUpdate(this, 'Comment') + this.shouldComponentUpdate = shouldComponentUpdate(this, 'Comment'); this.onShowReply = () => { - const {showReply} = this.state - this.setState({showReply: !showReply, showEdit: false}) - this.saveOnShow(!showReply ? 'reply' : null) - } + const { showReply } = this.state; + this.setState({ showReply: !showReply, showEdit: false }); + this.saveOnShow(!showReply ? 'reply' : null); + }; this.onShowEdit = () => { - const {showEdit} = this.state - this.setState({showEdit: !showEdit, showReply: false}) - this.saveOnShow(!showEdit ? 'edit' : null) - } - this.saveOnShow = (type) => { - if(process.env.BROWSER) { - const {cont} = this.props; - const content = cont.get(this.props.content) - const formId = content.get('author') + '/' + content.get('permlink') - if(type) - localStorage.setItem('showEditor-' + formId, JSON.stringify({type}, null, 0)) + const { showEdit } = this.state; + this.setState({ showEdit: !showEdit, showReply: false }); + this.saveOnShow(!showEdit ? 'edit' : null); + }; + this.saveOnShow = type => { + if (process.env.BROWSER) { + const { cont } = this.props; + const content = cont.get(this.props.content); + const formId = + content.get('author') + '/' + content.get('permlink'); + if (type) + localStorage.setItem( + 'showEditor-' + formId, + JSON.stringify({ type }, null, 0) + ); else { - localStorage.removeItem('showEditor-' + formId) - localStorage.removeItem('replyEditorData-' + formId + '-reply') - localStorage.removeItem('replyEditorData-' + formId + '-edit') + localStorage.removeItem('showEditor-' + formId); + localStorage.removeItem( + 'replyEditorData-' + formId + '-reply' + ); + localStorage.removeItem( + 'replyEditorData-' + formId + '-edit' + ); } } - } - this.saveOnShow = this.saveOnShow.bind(this) + }; + this.saveOnShow = this.saveOnShow.bind(this); this.onDeletePost = () => { - const {props: {deletePost}} = this + const { props: { deletePost } } = this; const content = this.props.cont.get(this.props.content); - deletePost(content.get('author'), content.get('permlink')) - } + deletePost(content.get('author'), content.get('permlink')); + }; this.toggleCollapsed = this.toggleCollapsed.bind(this); } componentWillMount() { - this.initEditor(this.props) + this.initEditor(this.props); this._checkHide(this.props); } componentDidMount() { if (window.location.hash == this.props.anchor_link) { - this.setState({highlight: true}); // eslint-disable-line react/no-did-mount-set-state + this.setState({ highlight: true }); // eslint-disable-line react/no-did-mount-set-state } } @@ -168,216 +179,291 @@ class CommentImpl extends React.Component { _checkHide(props) { const content = props.cont.get(props.content); if (content) { - const hide = hideSubtree(props.cont, props.content) - const gray = content.getIn(['stats', 'gray']) - if(hide) { - const {onHide} = this.props + const hide = hideSubtree(props.cont, props.content); + const gray = content.getIn(['stats', 'gray']); + if (hide) { + const { onHide } = this.props; // console.log('Comment --> onHide') - if(onHide) onHide() + if (onHide) onHide(); } - this.setState({hide, hide_body: hide || gray}) + this.setState({ hide, hide_body: hide || gray }); } } toggleCollapsed() { - this.setState({collapsed: !this.state.collapsed}); + this.setState({ collapsed: !this.state.collapsed }); } revealBody() { - this.setState({hide_body: false}); + this.setState({ hide_body: false }); } initEditor(props) { - if(this.state.PostReplyEditor) return - const {cont} = this.props; + if (this.state.PostReplyEditor) return; + const { cont } = this.props; const content = cont.get(props.content); - if (!content) return - const post = content.get('author') + '/' + content.get('permlink') - const PostReplyEditor = ReplyEditor(post + '-reply') - const PostEditEditor = ReplyEditor(post + '-edit') - if(process.env.BROWSER) { - const formId = post - let showEditor = localStorage.getItem('showEditor-' + formId) - if(showEditor) { - showEditor = JSON.parse(showEditor) - if(showEditor.type === 'reply') { - this.setState({showReply: true}) + if (!content) return; + const post = content.get('author') + '/' + content.get('permlink'); + const PostReplyEditor = ReplyEditor(post + '-reply'); + const PostEditEditor = ReplyEditor(post + '-edit'); + if (process.env.BROWSER) { + const formId = post; + let showEditor = localStorage.getItem('showEditor-' + formId); + if (showEditor) { + showEditor = JSON.parse(showEditor); + if (showEditor.type === 'reply') { + this.setState({ showReply: true }); } - if(showEditor.type === 'edit') { - this.setState({showEdit: true}) + if (showEditor.type === 'edit') { + this.setState({ showEdit: true }); } } } - this.setState({PostReplyEditor, PostEditEditor}) + this.setState({ PostReplyEditor, PostEditEditor }); } render() { - const {cont} = this.props; + const { cont } = this.props; const dis = cont.get(this.props.content); if (!dis) { - return
    {tt('g.loading')}...
    + return
    {tt('g.loading')}...
    ; } const comment = dis.toJS(); - if(!comment.stats) { - console.error('Comment -- missing stats object') - comment.stats = {} + if (!comment.stats) { + console.error('Comment -- missing stats object'); + comment.stats = {}; } - const {allowDelete, authorRepLog10, gray} = comment.stats - const {author, json_metadata} = comment - const {username, depth, anchor_link, - showNegativeComments, ignore_list, noImage} = this.props - const {onShowReply, onShowEdit, onDeletePost} = this - const post = comment.author + '/' + comment.permlink - const {PostReplyEditor, PostEditEditor, showReply, showEdit, hide, hide_body} = this.state - const Editor = showReply ? PostReplyEditor : PostEditEditor + const { allowDelete, authorRepLog10, gray } = comment.stats; + const { author, json_metadata } = comment; + const { + username, + depth, + anchor_link, + showNegativeComments, + ignore_list, + noImage, + } = this.props; + const { onShowReply, onShowEdit, onDeletePost } = this; + const post = comment.author + '/' + comment.permlink; + const { + PostReplyEditor, + PostEditEditor, + showReply, + showEdit, + hide, + hide_body, + } = this.state; + const Editor = showReply ? PostReplyEditor : PostEditEditor; - let {rootComment} = this.props - if(!rootComment && depth === 1) { + let { rootComment } = this.props; + if (!rootComment && depth === 1) { rootComment = comment.parent_author + '/' + comment.parent_permlink; } - const comment_link = `/${comment.category}/@${rootComment}#@${comment.author}/${comment.permlink}` - const ignore = ignore_list && ignore_list.has(comment.author) + const comment_link = `/${comment.category}/@${rootComment}#@${ + comment.author + }/${comment.permlink}`; + const ignore = ignore_list && ignore_list.has(comment.author); - if(!showNegativeComments && (hide || ignore)) { + if (!showNegativeComments && (hide || ignore)) { return null; } - let jsonMetadata = null + let jsonMetadata = null; try { - if(!showReply) jsonMetadata = JSON.parse(json_metadata) - } catch(error) { + if (!showReply) jsonMetadata = JSON.parse(json_metadata); + } catch (error) { // console.error('Invalid json metadata string', json_metadata, 'in post', this.props.content); } // const get_asset_value = ( asset_str ) => { return parseFloat( asset_str.split(' ')[0] ); } // const steem_supply = this.props.global.getIn(['props','current_supply']); // hide images if author is in blacklist - const hideImages = ImageUserBlockList.includes(author) + const hideImages = ImageUserBlockList.includes(author); - const showDeleteOption = username === author && allowDelete - const showEditOption = username === author - const showReplyOption = comment.depth < 255 - const archived = comment.cashout_time === '1969-12-31T23:59:59' // TODO: audit after HF19. #1259 - const readonly = archived || $STM_Config.read_only_mode + const showDeleteOption = username === author && allowDelete; + const showEditOption = username === author; + const showReplyOption = comment.depth < 255; + const archived = comment.cashout_time === '1969-12-31T23:59:59'; // TODO: audit after HF19. #1259 + const readonly = archived || $STM_Config.read_only_mode; let body = null; let controls = null; if (!this.state.collapsed && !hide_body) { - body = (); - controls = (
    - - - {showReplyOption && {tt('g.reply')}} - {' '}{!readonly && showEditOption && {tt('g.edit')}} - {' '}{!readonly && showDeleteOption && {tt('g.delete')}} - -
    ); + body = ( + + ); + controls = ( +
    + + + {showReplyOption && ( + {tt('g.reply')} + )}{' '} + {!readonly && + showEditOption && ( + {tt('g.edit')} + )}{' '} + {!readonly && + showDeleteOption && ( + {tt('g.delete')} + )} + +
    + ); } let replies = null; - if(!this.state.collapsed && comment.children > 0) { - if(depth > 7) { - const comment_permlink = `/${comment.category}/@${comment.author}/${comment.permlink}` - replies = Show {comment.children} more {comment.children == 1 ? 'reply' : 'replies'} + if (!this.state.collapsed && comment.children > 0) { + if (depth > 7) { + const comment_permlink = `/${comment.category}/@${ + comment.author + }/${comment.permlink}`; + replies = ( + + Show {comment.children} more{' '} + {comment.children == 1 ? 'reply' : 'replies'} + + ); } else { replies = comment.replies; - sortComments( cont, replies, this.props.sort_order ); + sortComments(cont, replies, this.props.sort_order); // When a comment has hidden replies and is collapsed, the reply count is off //console.log("replies:", replies.length, "num_visible:", replies.filter( reply => !cont.get(reply).getIn(['stats', 'hide'])).length) replies = replies.map((reply, idx) => ( - ) - ); + + )); } } - const commentClasses = ['hentry'] - commentClasses.push('Comment') - commentClasses.push(this.props.root ? 'root' : 'reply') - if(hide_body || this.state.collapsed) commentClasses.push('collapsed'); + const commentClasses = ['hentry']; + commentClasses.push('Comment'); + commentClasses.push(this.props.root ? 'root' : 'reply'); + if (hide_body || this.state.collapsed) commentClasses.push('collapsed'); - let innerCommentClass = ignore || gray ? 'downvoted' : '' - if(this.state.highlight) innerCommentClass += ' highlighted' + let innerCommentClass = ignore || gray ? 'downvoted' : ''; + if (this.state.highlight) innerCommentClass += ' highlighted'; //console.log(comment); let renderedEditor = null; if (showReply || showEdit) { - renderedEditor = (
    - { - this.setState({showReply: false, showEdit: false}) - this.saveOnShow(null) - }} - onCancel={() => { - this.setState({showReply: false, showEdit: false}) - this.saveOnShow(null) - }} - jsonMetadata={jsonMetadata} - /> -
    ) + renderedEditor = ( +
    + { + this.setState({ + showReply: false, + showEdit: false, + }); + this.saveOnShow(null); + }} + onCancel={() => { + this.setState({ + showReply: false, + showEdit: false, + }); + this.saveOnShow(null); + }} + jsonMetadata={jsonMetadata} + /> +
    + ); } const depth_indicator = []; if (depth > 1) { for (let i = 1; i < depth; ++i) { - depth_indicator.push(
    ·
    ); + depth_indicator.push( +
    + · +
    + ); } } return ( -
    - {depth_indicator} -
    -
    - -
    -
    - - -
    - -
    - -
    +
    + {depth_indicator} +
    +
    + +
    +
    + + +
    + +
    + +
      ·   - + - { (this.state.collapsed || hide_body) && - } - { this.state.collapsed && comment.children > 0 && - {tt('g.reply_count', {count: comment.children})}} - { !this.state.collapsed && hide_body && - {tt('g.reveal_comment')}} -
    -
    - {showEdit ? renderedEditor : body} -
    -
    - {controls} -
    -
    -
    - {showReply && renderedEditor} - {replies} + {(this.state.collapsed || hide_body) && ( + + )} + {this.state.collapsed && + comment.children > 0 && ( + + {tt('g.reply_count', { + count: comment.children, + })} + + )} + {!this.state.collapsed && + hide_body && ( + + {tt('g.reveal_comment')} + + )} +
    +
    + {showEdit ? renderedEditor : body} +
    +
    {controls}
    +
    +
    + {showReply && renderedEditor} + {replies} +
    -
    ); } } @@ -385,29 +471,40 @@ class CommentImpl extends React.Component { const Comment = connect( // mapStateToProps (state, ownProps) => { - const {content} = ownProps + const { content } = ownProps; - const username = state.user.getIn(['current', 'username']) - const ignore_list = username ? state.global.getIn(['follow', 'getFollowingAsync', username, 'ignore_result']) : null + const username = state.user.getIn(['current', 'username']); + const ignore_list = username + ? state.global.getIn([ + 'follow', + 'getFollowingAsync', + username, + 'ignore_result', + ]) + : null; return { ...ownProps, anchor_link: '#@' + content, // Using a hash here is not standard but intentional; see issue #124 for details username, - ignore_list - } + ignore_list, + }; }, // mapDispatchToProps dispatch => ({ - unlock: () => { dispatch(userActions.showLogin()) }, + unlock: () => { + dispatch(userActions.showLogin()); + }, deletePost: (author, permlink) => { - dispatch(transactionActions.broadcastOperation({ - type: 'delete_comment', - operation: {author, permlink}, - confirm: tt('g.are_you_sure'), - })) + dispatch( + transactionActions.broadcastOperation({ + type: 'delete_comment', + operation: { author, permlink }, + confirm: tt('g.are_you_sure'), + }) + ); }, }) -)(CommentImpl) +)(CommentImpl); export default Comment; diff --git a/src/app/components/cards/MarkdownViewer.jsx b/src/app/components/cards/MarkdownViewer.jsx index 044d021a5..8ecd596e8 100644 --- a/src/app/components/cards/MarkdownViewer.jsx +++ b/src/app/components/cards/MarkdownViewer.jsx @@ -1,11 +1,11 @@ import React from 'react'; -import {connect} from 'react-redux' -import {Component} from 'react' -import Remarkable from 'remarkable' -import YoutubePreview from 'app/components/elements/YoutubePreview' -import sanitizeConfig, {noImageText} from 'app/utils/SanitizeConfig' -import sanitize from 'sanitize-html' -import HtmlReady from 'shared/HtmlReady' +import { connect } from 'react-redux'; +import { Component } from 'react'; +import Remarkable from 'remarkable'; +import YoutubePreview from 'app/components/elements/YoutubePreview'; +import sanitizeConfig, { noImageText } from 'app/utils/SanitizeConfig'; +import sanitize from 'sanitize-html'; +import HtmlReady from 'shared/HtmlReady'; import tt from 'counterpart'; const remarkable = new Remarkable({ @@ -13,8 +13,8 @@ const remarkable = new Remarkable({ breaks: true, linkify: false, // linkify is done locally typographer: false, // https://github.com/jonschlinkert/remarkable/issues/142#issuecomment-221546793 - quotes: '“”‘’' -}) + quotes: '“”‘’', +}); class MarkdownViewer extends Component { static propTypes = { @@ -29,39 +29,44 @@ class MarkdownViewer extends Component { noImage: React.PropTypes.bool, allowDangerousHTML: React.PropTypes.bool, hideImages: React.PropTypes.bool, // whether to replace images with just a span containing the src url - // used for the ImageUserBlockList - } + // used for the ImageUserBlockList + }; static defaultProps = { className: '', large: false, allowDangerousHTML: false, hideImages: false, - } + }; constructor() { - super() - this.state = {allowNoImage: true} + super(); + this.state = { allowNoImage: true }; } shouldComponentUpdate(np, ns) { - return np.text !== this.props.text || - np.large !== this.props.large || - // np.formId !== this.props.formId || - np.canEdit !== this.props.canEdit || - ns.allowNoImage !== this.state.allowNoImage + return ( + np.text !== this.props.text || + np.large !== this.props.large || + // np.formId !== this.props.formId || + np.canEdit !== this.props.canEdit || + ns.allowNoImage !== this.state.allowNoImage + ); } onAllowNoImage = () => { - this.setState({allowNoImage: false}) - } + this.setState({ allowNoImage: false }); + }; render() { - const {noImage, hideImages} = this.props - const {allowNoImage} = this.state - let {text} = this.props - if (!text) text = '' // text can be empty, still view the link meta data - const {large, /*formId, canEdit, jsonMetadata,*/ highQualityPost} = this.props + const { noImage, hideImages } = this.props; + const { allowNoImage } = this.state; + let { text } = this.props; + if (!text) text = ''; // text can be empty, still view the link meta data + const { + large, + /*formId, canEdit, jsonMetadata,*/ highQualityPost, + } = this.props; let html = false; // See also ReplyEditor isHtmlTest @@ -71,95 +76,133 @@ class MarkdownViewer extends Component { text = m[1]; } else { // See also ReplyEditor isHtmlTest - html = /^

    [\S\s]*<\/p>/.test(text) + html = /^

    [\S\s]*<\/p>/.test(text); } // Strip out HTML comments. "JS-DOS" bug. - text = text.replace(/|$)/g, '(html comment removed: $1)') + text = text.replace( + /|$)/g, + '(html comment removed: $1)' + ); - let renderedText = html ? text : remarkable.render(text) + let renderedText = html ? text : remarkable.render(text); // Embed videos, link mentions and hashtags, etc... - if(renderedText) renderedText = HtmlReady(renderedText, {hideImages}).html + if (renderedText) + renderedText = HtmlReady(renderedText, { hideImages }).html; // Complete removal of javascript and other dangerous tags.. // The must remain as close as possible to dangerouslySetInnerHTML - let cleanText = renderedText + let cleanText = renderedText; if (this.props.allowDangerousHTML === true) { - console.log('WARN\tMarkdownViewer rendering unsanitized content') + console.log('WARN\tMarkdownViewer rendering unsanitized content'); } else { - cleanText = sanitize(renderedText, sanitizeConfig({large, highQualityPost, noImage: noImage && allowNoImage})) + cleanText = sanitize( + renderedText, + sanitizeConfig({ + large, + highQualityPost, + noImage: noImage && allowNoImage, + }) + ); } - if(/<\s*script/ig.test(cleanText)) { + if (/<\s*script/gi.test(cleanText)) { // Not meant to be complete checking, just a secondary trap and red flag (code can change) - console.error('Refusing to render script tag in post text', cleanText) - return

    + console.error( + 'Refusing to render script tag in post text', + cleanText + ); + return
    ; } - const noImageActive = cleanText.indexOf(noImageText) !== -1 + const noImageActive = cleanText.indexOf(noImageText) !== -1; // In addition to inserting the youtube compoennt, this allows react to compare separately preventing excessive re-rendering. - let idx = 0 - const sections = [] + let idx = 0; + const sections = []; // HtmlReady inserts ~~~ embed:${id} type ~~~ - for(let section of cleanText.split('~~~ embed:')) { - const match = section.match(/^([A-Za-z0-9\_\-]+) (youtube|vimeo) ~~~/) - if(match && match.length >= 3) { - const id = match[1] - const type = match[2] + for (let section of cleanText.split('~~~ embed:')) { + const match = section.match( + /^([A-Za-z0-9\_\-]+) (youtube|vimeo) ~~~/ + ); + if (match && match.length >= 3) { + const id = match[1]; + const type = match[2]; const w = large ? 640 : 480, - h = large ? 360 : 270 - if(type === 'youtube') { - sections.push( - - ) - } else if(type === 'vimeo') { - const url = `https://player.vimeo.com/video/${id}` + h = large ? 360 : 270; + if (type === 'youtube') { sections.push( -
    - + const autoPlaySrc = `https://www.youtube.com/embed/${ + youTubeId + }?autoplay=1&autohide=1&${dataParams}`; + return ( +
    + `, + ``, -``, + ``, -``, + ``, -``, + ``, -`
    • XSS`, + `
      • XSS`, -``, + ``, -``, + ``, -``, + ``, -`
        *
        `, + `
        *
        `, -`XSS`, + `XSS`, -`';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//"; + `';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//"; alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//-- >">'>`, -`'';!--"=&{()}`, + `'';!--"=&{()}`, -``, + ``, -`onnerr w/ clearly invalid img:
        + `onnerr w/ clearly invalid img:
        good image:
        good url, bad img: (results will vary if using image proxy -- it rewrites 'src')`, -`**test**!%3Cimg%20src=%22awsome.jpg%22%20onerror=%22alert(1)%22/%3E`, - -`test!%3Cimg%20src=%22awsome.jpg%22%20onerror=%22alert(1)%22/%3E`, + `**test**!%3Cimg%20src=%22awsome.jpg%22%20onerror=%22alert(1)%22/%3E`, -'', + `test!%3Cimg%20src=%22awsome.jpg%22%20onerror=%22alert(1)%22/%3E`, -'Hax', + '', -'Link to a local page with bad rel attr', -'Link to domain (relative protocol) and bad target attr', + 'Hax', -] + 'Link to a local page with bad rel attr', + 'Link to domain (relative protocol) and bad target attr', +]; diff --git a/src/app/locales/counterpart/es.js b/src/app/locales/counterpart/es.js index a43372846..f5640c7d2 100644 --- a/src/app/locales/counterpart/es.js +++ b/src/app/locales/counterpart/es.js @@ -9,22 +9,22 @@ module.exports = { formats: { date: { - 'default': '%a, %e %b %Y', - long: '%A, %B %o, %Y', - short: '%b %e' + default: '%a, %e %b %Y', + long: '%A, %B %o, %Y', + short: '%b %e', }, time: { - 'default': '%H:%M', - long: '%H:%M:%S %z', - short: '%H:%M' + default: '%H:%M', + long: '%H:%M:%S %z', + short: '%H:%M', }, datetime: { - 'default': '%a, %e %b %Y %H:%M', - long: '%A, %B %o, %Y %H:%M:%S %z', - short: '%e %b %H:%M' - } - } - } + default: '%a, %e %b %Y %H:%M', + long: '%A, %B %o, %Y %H:%M:%S %z', + short: '%e %b %H:%M', + }, + }, + }, }; diff --git a/src/app/locales/counterpart/fr.js b/src/app/locales/counterpart/fr.js index a43372846..f5640c7d2 100644 --- a/src/app/locales/counterpart/fr.js +++ b/src/app/locales/counterpart/fr.js @@ -9,22 +9,22 @@ module.exports = { formats: { date: { - 'default': '%a, %e %b %Y', - long: '%A, %B %o, %Y', - short: '%b %e' + default: '%a, %e %b %Y', + long: '%A, %B %o, %Y', + short: '%b %e', }, time: { - 'default': '%H:%M', - long: '%H:%M:%S %z', - short: '%H:%M' + default: '%H:%M', + long: '%H:%M:%S %z', + short: '%H:%M', }, datetime: { - 'default': '%a, %e %b %Y %H:%M', - long: '%A, %B %o, %Y %H:%M:%S %z', - short: '%e %b %H:%M' - } - } - } + default: '%a, %e %b %Y %H:%M', + long: '%A, %B %o, %Y %H:%M:%S %z', + short: '%e %b %H:%M', + }, + }, + }, }; diff --git a/src/app/locales/counterpart/it.js b/src/app/locales/counterpart/it.js index 88edb1dd0..0c5799a5b 100644 --- a/src/app/locales/counterpart/it.js +++ b/src/app/locales/counterpart/it.js @@ -9,22 +9,22 @@ module.exports = { formats: { date: { - 'default': '%a, %e %b %Y', - long: '%A, %B %o, %Y', - short: '%e %b' + default: '%a, %e %b %Y', + long: '%A, %B %o, %Y', + short: '%e %b', }, time: { - 'default': '%H:%M', - long: '%H:%M:%S %z', - short: '%H:%M' + default: '%H:%M', + long: '%H:%M:%S %z', + short: '%H:%M', }, datetime: { - 'default': '%a, %e %b %Y %H:%M', - long: '%A, %B %o, %Y %H:%M:%S %z', - short: '%e %b %H:%M' - } - } - } + default: '%a, %e %b %Y %H:%M', + long: '%A, %B %o, %Y %H:%M:%S %z', + short: '%e %b %H:%M', + }, + }, + }, }; diff --git a/src/app/locales/counterpart/ko.js b/src/app/locales/counterpart/ko.js index a43372846..f5640c7d2 100644 --- a/src/app/locales/counterpart/ko.js +++ b/src/app/locales/counterpart/ko.js @@ -9,22 +9,22 @@ module.exports = { formats: { date: { - 'default': '%a, %e %b %Y', - long: '%A, %B %o, %Y', - short: '%b %e' + default: '%a, %e %b %Y', + long: '%A, %B %o, %Y', + short: '%b %e', }, time: { - 'default': '%H:%M', - long: '%H:%M:%S %z', - short: '%H:%M' + default: '%H:%M', + long: '%H:%M:%S %z', + short: '%H:%M', }, datetime: { - 'default': '%a, %e %b %Y %H:%M', - long: '%A, %B %o, %Y %H:%M:%S %z', - short: '%e %b %H:%M' - } - } - } + default: '%a, %e %b %Y %H:%M', + long: '%A, %B %o, %Y %H:%M:%S %z', + short: '%e %b %H:%M', + }, + }, + }, }; diff --git a/src/app/locales/en.json b/src/app/locales/en.json index 78427ea56..e5b36ed86 100644 --- a/src/app/locales/en.json +++ b/src/app/locales/en.json @@ -1,654 +1,775 @@ { - "g": { - "age": "age", - "amount": "Amount", - "and": "and", - "are_you_sure": "Are you sure?", - "ask": "Ask", - "balance": "Balance", - "balances": "Balances", - "bid": "Bid", - "blog": "Blog", - "browse": "Browse", - "buy": "Buy", - "buy_or_sell": "Buy or Sell", - "by": "by", - "cancel": "Cancel", - "change_password": "Change Password", - "choose_language": "Choose Language", - "clear": "Clear", - "close": "Close", - "collapse_or_expand": "Collapse/Expand", - "comments": "Comments", - "confirm": "Confirm", - "convert": "Convert", - "date": "Date", - "delete": "Delete", - "dismiss": "Dismiss", - "edit": "Edit", - "email": "Email", - "feed": "Feed", - "follow": "Follow", - "for": " for ", - "from": " from ", - "go_back": "Back", - "hide": "Hide", - "in": "in", - "in_reply_to": "in reply to", - "insufficient_balance": "Insufficient balance", - "invalid_amount": "Invalid amount", - "joined": "Joined", - "loading": "Loading", - "login": "Login", - "logout": "Logout", - "memo": "Memo", - "mute": "Mute", - "myblog": "My blog", - "mycomments": "My comments", - "myreplies": "Replies to me", - "new": "new", - "newer": "Newer", - "next": "Next", - "no": "No", - "ok": "Ok", - "older": "Older", - "or": "or", - "order_placed": "Order placed", - "password": "Password", - "payouts": "Payouts", - "permissions": "Permissions", - "phishy_message": "Link expanded to plain text; beware of a potential phishing attempt", - "post": "Post", - "post_as": "Post as", - "posts": "Posts", - "powered_up_100": "Powered Up 100%%", - "preview": "Preview", - "previous": "Previous", - "price": "Price", - "print": "Print", - "promote": "Promote", - "promoted": "promoted", - "re": "RE", - "re_to": "RE: %(topic)s", - "recent_password": "Recent Password", - "receive": "Receive ", - "remove": "Remove", - "remove_vote": "Remove Vote", - "replied_to": "replied to %(account)s", - "replies": "Replies", - "reply": "Reply", - "reply_count": { - "zero": "no replies", - "one": "1 reply", - "other": "%(count)s replies" - }, - "reputation": "Reputation", - "reveal_comment": "Reveal Comment", - "request": "request", - "required": "Required", - "rewards": "Rewards", - "save": "Save", - "saved": "Saved", - "search": "Search", - "sell": "Sell", - "settings": "Settings", - "share_this_post": "Share this post", - "show": "Show", - "sign_in": "Sign in", - "sign_up": "Sign up", - "since": "since", - "submit": "Submit", - "power_up": "Power Up", - "submit_a_story": "Post", - "tag": "Tag", - "to": " to ", - "topics": "Topics", - "toggle_nightmode": "Toggle Night Mode", - "all_tags": "All tags", - "transfer": "Transfer ", - "trending_topics": "Trending Topics", - "type": "Type", - "unfollow": "Unfollow", - "unmute": "Unmute", - "unknown": "Unknown", - "upvote": "Upvote", - "upvote_post": "Upvote post", - "username": "Username", - "version": "Version", - "vote": "Vote", - "votes": "votes", - "wallet": "Wallet", - "warning": "warning", - "yes": "Yes", - "posting": "Posting", - "owner": "Owner", - "active": "Active", - "account_not_found": "Account not found", - "this_is_wrong_password": "This is the wrong password", - "do_you_need_to": "Do you need to", - "account_name": "Account Name", - "recover_your_account": "recover your account", - "reset_usernames_password": "Reset %(username)s's Password", - "this_will_update_usernames_authtype_key": "This will update %(username)s %(authType)s key", - "passwords_do_not_match": "Passwords do not match", - "you_need_private_password_or_key_not_a_public_key": "You need a private password or key (not a public key)", - "the_rules_of_APP_NAME": { - "one": "The first rule of %(APP_NAME)s is: Do not lose your password.", - "second": "The second rule of %(APP_NAME)s is: Do not lose your password.", - "third": "The third rule of %(APP_NAME)s is: We cannot recover your password.", - "fourth": "The fourth rule: If you can remember the password, it's not secure.", - "fifth": "The fifth rule: Use only randomly-generated passwords.", - "sixth": "The sixth rule: Do not tell anyone your password.", - "seventh": "The seventh rule: Always back up your password." - }, - "recover_password": "Recover Account", - "current_password": "Current Password", - "generated_password": "Generated Password", - "backup_password_by_storing_it": "Back it up by storing in your password manager or a text file", - "enter_account_show_password": "Enter a valid account name to show the password", - "click_to_generate_password": "Click to generate password", - "re_enter_generate_password": "Re-enter Generated Password", - "understand_that_APP_NAME_cannot_recover_password": "I understand that %(APP_NAME)s cannot recover lost passwords", - "i_saved_password": "I have securely saved my generated password", - "update_password": "Update Password", - "confirm_password": "Confirm Password", - "account_updated": "Account Updated", - "password_must_be_characters_or_more": "Password must be %(amount)s characters or more", - "need_password_or_key": "You need a private password or key (not a public key)", - "login_to_see_memo": "login to see memo", - "new_password": "New Password", - "incorrect_password": "Incorrect password", - "username_does_not_exist": "Username does not exist", - "account_name_should_start_with_a_letter": "Account name should start with a letter.", - "account_name_should_be_shorter": "Account name should be shorter.", - "account_name_should_be_longer": "Account name should be longer.", - "account_name_should_have_only_letters_digits_or_dashes": "Account name should have only letters, digits, or dashes.", - "cannot_increase_reward_of_post_within_the_last_minute_before_payout": "Cannot increase reward of post within the last minute before payout", - "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": "vote currently exists, user must be indicate a desire to reject witness", - "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": "Only one Steem account allowed per IP address every 10 minutes", - "resteem_this_post": "Resteem This Post", - "reblog": "Resteem", - "write_your_story": "Write your story", - "remember_voting_and_posting_key": "Remember voting & posting key", - "auto_login_question_mark": "Auto login?", - "hide_private_key": "Hide private key", - "show_private_key": "Show private key", - "login_to_show": "Login to show", - "not_valid_email": "Not valid email", - "thank_you_for_being_an_early_visitor_to_APP_NAME": "Thank you for being an early visitor to %(APP_NAME)s. We will get back to you at the earliest possible opportunity.", - "author_rewards": "Author rewards", - "curation_rewards": "Curation rewards", - "sorry_your_reddit_account_doesnt_have_enough_karma": "Sorry, your Reddit account doesn't have enough Reddit Karma to qualify for a free sign up. Please add your email for a place on the waiting list", - "register_with_facebook": "Register with Facebook", - "or_click_the_button_below_to_register_with_facebook": "Or click the button below to register with Facebook", - "server_returned_error": "server returned error", - "APP_NAME_support": "%(APP_NAME)s Support", - "please_email_questions_to": "Please email your questions to", - "next_7_strings_single_block": { - "authors_get_paid_when_people_like_you_upvote_their_post": "Authors get paid when people like you upvote their post", - "if_you_enjoyed_what_you_read_earn_amount": "If you enjoyed what you read here, create your account today and start earning FREE STEEM!", - "free_steem": "FREE STEEM!", - "sign_up_earn_steem": "Sign up now to earn " - }, - "next_3_strings_together": { - "show_more": "Show more", - "show_less": "Show fewer", - "value_posts": "low value posts" - }, - "read_only_mode": "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", - "tags_and_topics": "Tags", - "show_more_topics": "View all tags", - "basic": "Basic", - "advanced": "Advanced", - "views": { - "zero": "No Views", - "one": "%(count)s View", - "other": "%(count)s Views" - }, - "responses": { - "zero": "No Responses", - "one": "%(count)s Response", - "other": "%(count)s Responses" - }, - "post_key_warning": { - "confirm": "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", - "warning": "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", - "checkbox": "I understand" - } - }, - "navigation": { - "about": "About", - "explore": "Explore", - "APP_NAME_whitepaper": "%(APP_NAME)s Whitepaper", - "buy_LIQUID_TOKEN": "Buy %(LIQUID_TOKEN)s", - "sell_LIQUID_TOKEN": "Sell %(LIQUID_TOKEN)s", - "currency_market": "Currency Market", - "stolen_account_recovery": "Stolen Accounts Recovery", - "change_account_password": "Change Account Password", - "witnesses": "Witnesses", - "vote_for_witnesses": "Vote for Witnesses", - "privacy_policy": "Privacy Policy", - "terms_of_service": "Terms of Service", - "sign_up": "Join", - "learn_more": "Learn More", - "welcome": "Welcome", - "faq": "FAQ", - "shop": "The Steemit Shop", - "chat": "Steemit Chat", - "app_center": "Steemit App Center", - "api_docs": "Steemit API Docs", - "bluepaper": "Steem Bluepaper", - "smt_whitepaper": "SMT Whitepaper", - "whitepaper": "Steem Whitepaper", - "intro_tagline": "Money talks", - "intro_paragraph": "Your voice is worth something. Join the community that pays you to post and curate high quality content." - }, - "main_menu": { - "hot": "hot", - "trending": "trending" - }, - "reply_editor": { - "shorten_title": "Shorten title", - "exceeds_maximum_length": "Exceeds maximum length (%(maxKb)sKB)", - "including_the_category": " (including the category '%(rootCategory)s')", - "use_limited_amount_of_tags": "You have %(tagsLength)s tags total%(includingCategory)s. Please use only 5 in your post and category line.", - "are_you_sure_you_want_to_clear_this_form": "Are you sure you want to clear this form?", - "uploading": "Uploading", - "draft_saved": "Draft saved.", - "editor": "Editor", - "insert_images_by_dragging_dropping": "Insert images by dragging & dropping, ", - "pasting_from_the_clipboard": "pasting from the clipboard, ", - "selecting_them": "selecting them", - "image_upload": "Image upload", - "power_up_100": "Power Up 100%%", - "default_50_50": "Default (50%% / 50%%)", - "decline_payout": "Decline Payout", - "check_this_to_auto_upvote_your_post": "Check this to auto-upvote your post", - "markdown_styling_guide": "Markdown Styling Guide", - "or_by": "or by", - "title": "Title", - "update_post": "Update Post", - "markdown_not_supported": "Markdown is not supported here" - }, - "category_selector_jsx": { - "tag_your_story": "Tag (up to 5 tags), the first tag is your main category.", - "select_a_tag": "Select a tag", - "maximum_tag_length_is_24_characters": "Maximum tag length is 24 characters", - "use_limited_amount_of_categories": "Please use only %(amount)s categories", - "use_only_lowercase_letters": "Use only lowercase letters", - "use_one_dash": "Use only one dash", - "use_spaces_to_separate_tags": "Use spaces to separate tags", - "use_only_allowed_characters": "Use only lowercase letters, digits and one dash", - "must_start_with_a_letter": "Must start with a letter", - "must_end_with_a_letter_or_number": "Must end with a letter or number" - }, - "postfull_jsx": { - "this_post_is_not_available_due_to_a_copyright_claim": "This post is not available due to a copyright claim.", - "share_on_facebook": "Share on Facebook", - "share_on_twitter": "Share on Twitter", - "share_on_linkedin": "Share on Linkedin", - "recent_password": "Recent Password", - "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": "In 3.5 days, convert %(amount)s %(DEBT_TOKEN)s into %(LIQUID_TOKEN)s", - "view_the_full_context": "View the full context", - "view_the_direct_parent": "View the direct parent", - "you_are_viewing_a_single_comments_thread_from": "You are viewing a single comment's thread from" - }, - "market_jsx": { - "action": "Action", - "date_created": "Date Created", - "last_price": "Last price", - "24h_volume": "24h volume", - "spread": "Spread", - "total": "Total", - "available": "Available", - "lowest_ask": "Lowest ask", - "highest_bid": "Highest bid", - "buy_orders": "Buy Orders", - "sell_orders": "Sell Orders", - "trade_history": "Trade History", - "open_orders": "Open Orders", - "sell_amount_for_atleast": "Sell %(amount_to_sell)s for at least %(min_to_receive)s (%(effectivePrice)s)", - "buy_atleast_amount_for": "Buy at least %(min_to_receive)s for %(amount_to_sell)s (%(effectivePrice)s)", - "price_warning_above": "This price is well above the current market price of %(marketPrice)s, are you sure?", - "price_warning_below": "This price is well below the current market price of %(marketPrice)s, are you sure?", - "order_cancel_confirm": "Cancel order %(order_id)s from %(user)s?", - "order_cancelled": "Order %(order_id)s cancelled.", - "higher": "Higher", - "lower": "Lower", - "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" - }, - "recoveraccountstep1_jsx": { - "begin_recovery": "Begin Recovery", - "not_valid": "Not valid", - "account_name_is_not_found": "Account name is not found", - "unable_to_recover_account_not_change_ownership_recently": "We are unable to recover this account, it has not changed ownership recently.", - "password_not_used_in_last_days": "This password was not used on this account in the last 30 days.", - "request_already_submitted_contact_support": "Your request has been already submitted and we are working on it. Please contact %(SUPPORT_EMAIL)s for the status of your request.", - "recover_account_intro": "From time to time, a Steemian's owner key may be compromised. Stolen Account Recovery gives the rightful account owner 30 days to recover their account from the moment the thief changed their owner key. Stolen Account Recovery can only be used on %(APP_URL)s if the account owner had previously listed '%(APP_NAME)s' as their account trustee and complied with %(APP_NAME)s's Terms of Service.", - "login_with_facebook_or_reddit_media_to_verify_identity": "Please login with Facebook or Reddit to verify your identity", - "login_with_social_media_to_verify_identity": "Please login with %(provider)s to verify your identity", - "enter_email_toverify_identity": "We need to verify your identity. Please enter your email address below to begin the verification.", - "continue_with_email": "Continue with Email", - "thanks_for_submitting_request_for_account_recovery": "Thanks for submitting your request for Account Recovery using %(APP_NAME)s's blockchain-based multi factor authentication. We will respond to you as quickly as possible, however, please expect there may be some delay in response due to high volume of emails. Please be prepared to verify your identity.", - "recovering_account": "Recovering account", - "recover_account": "Recover Account", - "checking_account_owner": "Checking account owner", - "sending_recovery_request": "Sending recovery request", - "cant_confirm_account_ownership": "We can't confirm account ownership. Check your password", - "account_recovery_request_not_confirmed": "Account recovery request is not confirmed yet, please get back later, thank you for your patience." - }, - "user_profile": { - "unknown_account": "Unknown Account", - "user_hasnt_made_any_posts_yet": "Looks like %(name)s hasn't made any posts yet!", - "user_hasnt_started_bloggin_yet": "Looks like %(name)s hasn't started blogging yet!", - "user_hasnt_followed_anything_yet": "Looks like %(name)s might not be following anyone yet! If %(name)s recently added new users to follow, their personalized feed will populate once new content is available.", - "user_hasnt_had_any_replies_yet": "%(name)s hasn't had any replies yet", - "looks_like_you_havent_posted_anything_yet": "Looks like you haven't posted anything yet.", - "create_a_post": "Create a Post", - "explore_trending_articles": "Explore Trending Articles", - "read_the_quick_start_guide": "Read The Quick Start Guide", - "browse_the_faq": "Browse The FAQ", - "followers": "Followers", - "this_is_users_reputations_score_it_is_based_on_history_of_votes": "This is %(name)s's reputation score.\n\nThe reputation score is based on the history of votes received by the account, and is used to hide low quality content.", - "follower_count": { - "zero": "No followers", - "one": "1 follower", - "other": "%(count)s followers" - }, - "followed_count": { - "zero": "Not following anybody", - "one": "1 following", - "other": "%(count)s following" - }, - "post_count": { - "zero": "No posts", - "one": "1 post", - "other": "%(count)s posts" - } - }, - "authorrewards_jsx": { - "estimated_author_rewards_last_week": "Estimated author rewards last week", - "author_rewards_history": "Author Rewards History" - }, - "curationrewards_jsx": { - "estimated_curation_rewards_last_week": "Estimated curation rewards last week", - "curation_rewards_history": "Curation Rewards History" - }, - "post_jsx": { - "now_showing_comments_with_low_ratings": "Now showing comments with low ratings", - "sort_order": "Sort Order", - "comments_were_hidden_due_to_low_ratings": "Comments were hidden due to low ratings" - }, - "voting_jsx": { - "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": "Flagging a post can remove rewards and make this material less visible. Some common reasons to flag", - "disagreement_on_rewards": "Disagreement on rewards", - "fraud_or_plagiarism": "Fraud or Plagiarism", - "hate_speech_or_internet_trolling": "Hate Speech or Internet Trolling", - "intentional_miss_categorized_content_or_spam": "Intentional miss-categorized content or Spam", - "pending_payout": "Pending Payout $%(value)s", - "payout_declined": "Payout Declined", - "max_accepted_payout": "Max Accepted Payout $%(value)s", - "promotion_cost": "Promotion Cost $%(value)s", - "past_payouts": "Past Payouts $%(value)s", - "past_payouts_author": " - Author $%(value)s", - "past_payouts_curators": " - Curators $%(value)s", - "we_will_reset_curation_rewards_for_this_post": "will reset your curation rewards for this post", - "removing_your_vote": "Removing your vote", - "changing_to_an_upvote": "Changing to an Up-Vote", - "changing_to_a_downvote": "Changing to a Down-Vote", - "confirm_flag": "Confirm Flag", - "and_more": "and %(count)s more", - "votes_plural": { - "one": "%(count)s vote", - "other": "%(count)s votes" - } - }, - "witnesses_jsx": { - "witness_thread": "witness thread", - "top_witnesses": "Witness Voting", - "you_have_votes_remaining": { - "zero": "You have no votes remaining", - "one": "You have 1 vote remaining", - "other": "You have %(count)s votes remaining" - }, - "you_can_vote_for_maximum_of_witnesses": "You can vote for a maximum of 30 witnesses", - "witness": "Witness", - "information": "Information", - "if_you_want_to_vote_outside_of_top_enter_account_name": "If you would like to vote for a witness outside of the top 50, enter the account name below to cast a vote", - "set_witness_proxy": "You can also choose a proxy that will vote for witnesses for you. This will reset your current witness selection.", - "witness_set": "You have set a voting proxy. If you would like to re-enable manual voting, please clear your proxy.", - "witness_proxy_current": "Your current proxy is", - "witness_proxy_set": "Set proxy", - "witness_proxy_clear": "Clear proxy", - "proxy_update_error": "Your proxy was not updated" - }, - "votesandcomments_jsx": { - "no_responses_yet_click_to_respond": "No responses yet. Click to respond.", - "response_count_tooltip": { - "zero": "no responses. Click to respond.", - "one": "1 response. Click to respond.", - "other": "%(count)s responses. Click to respond." - }, - "vote_count": { - "zero": "no votes", - "one": "1 vote", - "other": "%(count)s votes" + "g": { + "age": "age", + "amount": "Amount", + "and": "and", + "are_you_sure": "Are you sure?", + "ask": "Ask", + "balance": "Balance", + "balances": "Balances", + "bid": "Bid", + "blog": "Blog", + "browse": "Browse", + "buy": "Buy", + "buy_or_sell": "Buy or Sell", + "by": "by", + "cancel": "Cancel", + "change_password": "Change Password", + "choose_language": "Choose Language", + "clear": "Clear", + "close": "Close", + "collapse_or_expand": "Collapse/Expand", + "comments": "Comments", + "confirm": "Confirm", + "convert": "Convert", + "date": "Date", + "delete": "Delete", + "dismiss": "Dismiss", + "edit": "Edit", + "email": "Email", + "feed": "Feed", + "follow": "Follow", + "for": " for ", + "from": " from ", + "go_back": "Back", + "hide": "Hide", + "in": "in", + "in_reply_to": "in reply to", + "insufficient_balance": "Insufficient balance", + "invalid_amount": "Invalid amount", + "joined": "Joined", + "loading": "Loading", + "login": "Login", + "logout": "Logout", + "memo": "Memo", + "mute": "Mute", + "myblog": "My blog", + "mycomments": "My comments", + "myreplies": "Replies to me", + "new": "new", + "newer": "Newer", + "next": "Next", + "no": "No", + "ok": "Ok", + "older": "Older", + "or": "or", + "order_placed": "Order placed", + "password": "Password", + "payouts": "Payouts", + "permissions": "Permissions", + "phishy_message": + "Link expanded to plain text; beware of a potential phishing attempt", + "post": "Post", + "post_as": "Post as", + "posts": "Posts", + "powered_up_100": "Powered Up 100%%", + "preview": "Preview", + "previous": "Previous", + "price": "Price", + "print": "Print", + "promote": "Promote", + "promoted": "promoted", + "re": "RE", + "re_to": "RE: %(topic)s", + "recent_password": "Recent Password", + "receive": "Receive ", + "remove": "Remove", + "remove_vote": "Remove Vote", + "replied_to": "replied to %(account)s", + "replies": "Replies", + "reply": "Reply", + "reply_count": { + "zero": "no replies", + "one": "1 reply", + "other": "%(count)s replies" + }, + "reputation": "Reputation", + "reveal_comment": "Reveal Comment", + "request": "request", + "required": "Required", + "rewards": "Rewards", + "save": "Save", + "saved": "Saved", + "search": "Search", + "sell": "Sell", + "settings": "Settings", + "share_this_post": "Share this post", + "show": "Show", + "sign_in": "Sign in", + "sign_up": "Sign up", + "since": "since", + "submit": "Submit", + "power_up": "Power Up", + "submit_a_story": "Post", + "tag": "Tag", + "to": " to ", + "topics": "Topics", + "toggle_nightmode": "Toggle Night Mode", + "all_tags": "All tags", + "transfer": "Transfer ", + "trending_topics": "Trending Topics", + "type": "Type", + "unfollow": "Unfollow", + "unmute": "Unmute", + "unknown": "Unknown", + "upvote": "Upvote", + "upvote_post": "Upvote post", + "username": "Username", + "version": "Version", + "vote": "Vote", + "votes": "votes", + "wallet": "Wallet", + "warning": "warning", + "yes": "Yes", + "posting": "Posting", + "owner": "Owner", + "active": "Active", + "account_not_found": "Account not found", + "this_is_wrong_password": "This is the wrong password", + "do_you_need_to": "Do you need to", + "account_name": "Account Name", + "recover_your_account": "recover your account", + "reset_usernames_password": "Reset %(username)s's Password", + "this_will_update_usernames_authtype_key": + "This will update %(username)s %(authType)s key", + "passwords_do_not_match": "Passwords do not match", + "you_need_private_password_or_key_not_a_public_key": + "You need a private password or key (not a public key)", + "the_rules_of_APP_NAME": { + "one": + "The first rule of %(APP_NAME)s is: Do not lose your password.", + "second": + "The second rule of %(APP_NAME)s is: Do not lose your password.", + "third": + "The third rule of %(APP_NAME)s is: We cannot recover your password.", + "fourth": + "The fourth rule: If you can remember the password, it's not secure.", + "fifth": "The fifth rule: Use only randomly-generated passwords.", + "sixth": "The sixth rule: Do not tell anyone your password.", + "seventh": "The seventh rule: Always back up your password." + }, + "recover_password": "Recover Account", + "current_password": "Current Password", + "generated_password": "Generated Password", + "backup_password_by_storing_it": + "Back it up by storing in your password manager or a text file", + "enter_account_show_password": + "Enter a valid account name to show the password", + "click_to_generate_password": "Click to generate password", + "re_enter_generate_password": "Re-enter Generated Password", + "understand_that_APP_NAME_cannot_recover_password": + "I understand that %(APP_NAME)s cannot recover lost passwords", + "i_saved_password": "I have securely saved my generated password", + "update_password": "Update Password", + "confirm_password": "Confirm Password", + "account_updated": "Account Updated", + "password_must_be_characters_or_more": + "Password must be %(amount)s characters or more", + "need_password_or_key": + "You need a private password or key (not a public key)", + "login_to_see_memo": "login to see memo", + "new_password": "New Password", + "incorrect_password": "Incorrect password", + "username_does_not_exist": "Username does not exist", + "account_name_should_start_with_a_letter": + "Account name should start with a letter.", + "account_name_should_be_shorter": "Account name should be shorter.", + "account_name_should_be_longer": "Account name should be longer.", + "account_name_should_have_only_letters_digits_or_dashes": + "Account name should have only letters, digits, or dashes.", + "cannot_increase_reward_of_post_within_the_last_minute_before_payout": + "Cannot increase reward of post within the last minute before payout", + "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": + "vote currently exists, user must be indicate a desire to reject witness", + "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": + "Only one Steem account allowed per IP address every 10 minutes", + "resteem_this_post": "Resteem This Post", + "reblog": "Resteem", + "write_your_story": "Write your story", + "remember_voting_and_posting_key": "Remember voting & posting key", + "auto_login_question_mark": "Auto login?", + "hide_private_key": "Hide private key", + "show_private_key": "Show private key", + "login_to_show": "Login to show", + "not_valid_email": "Not valid email", + "thank_you_for_being_an_early_visitor_to_APP_NAME": + "Thank you for being an early visitor to %(APP_NAME)s. We will get back to you at the earliest possible opportunity.", + "author_rewards": "Author rewards", + "curation_rewards": "Curation rewards", + "sorry_your_reddit_account_doesnt_have_enough_karma": + "Sorry, your Reddit account doesn't have enough Reddit Karma to qualify for a free sign up. Please add your email for a place on the waiting list", + "register_with_facebook": "Register with Facebook", + "or_click_the_button_below_to_register_with_facebook": + "Or click the button below to register with Facebook", + "server_returned_error": "server returned error", + "APP_NAME_support": "%(APP_NAME)s Support", + "please_email_questions_to": "Please email your questions to", + "next_7_strings_single_block": { + "authors_get_paid_when_people_like_you_upvote_their_post": + "Authors get paid when people like you upvote their post", + "if_you_enjoyed_what_you_read_earn_amount": + "If you enjoyed what you read here, create your account today and start earning FREE STEEM!", + "free_steem": "FREE STEEM!", + "sign_up_earn_steem": "Sign up now to earn " + }, + "next_3_strings_together": { + "show_more": "Show more", + "show_less": "Show fewer", + "value_posts": "low value posts" + }, + "read_only_mode": + "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", + "tags_and_topics": "Tags", + "show_more_topics": "View all tags", + "basic": "Basic", + "advanced": "Advanced", + "views": { + "zero": "No Views", + "one": "%(count)s View", + "other": "%(count)s Views" + }, + "responses": { + "zero": "No Responses", + "one": "%(count)s Response", + "other": "%(count)s Responses" + }, + "post_key_warning": { + "confirm": + "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", + "warning": + "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", + "checkbox": "I understand" + } + }, + "navigation": { + "about": "About", + "explore": "Explore", + "APP_NAME_whitepaper": "%(APP_NAME)s Whitepaper", + "buy_LIQUID_TOKEN": "Buy %(LIQUID_TOKEN)s", + "sell_LIQUID_TOKEN": "Sell %(LIQUID_TOKEN)s", + "currency_market": "Currency Market", + "stolen_account_recovery": "Stolen Accounts Recovery", + "change_account_password": "Change Account Password", + "witnesses": "Witnesses", + "vote_for_witnesses": "Vote for Witnesses", + "privacy_policy": "Privacy Policy", + "terms_of_service": "Terms of Service", + "sign_up": "Join", + "learn_more": "Learn More", + "welcome": "Welcome", + "faq": "FAQ", + "shop": "The Steemit Shop", + "chat": "Steemit Chat", + "app_center": "Steemit App Center", + "api_docs": "Steemit API Docs", + "bluepaper": "Steem Bluepaper", + "smt_whitepaper": "SMT Whitepaper", + "whitepaper": "Steem Whitepaper", + "intro_tagline": "Money talks", + "intro_paragraph": + "Your voice is worth something. Join the community that pays you to post and curate high quality content." + }, + "main_menu": { + "hot": "hot", + "trending": "trending" + }, + "reply_editor": { + "shorten_title": "Shorten title", + "exceeds_maximum_length": "Exceeds maximum length (%(maxKb)sKB)", + "including_the_category": + " (including the category '%(rootCategory)s')", + "use_limited_amount_of_tags": + "You have %(tagsLength)s tags total%(includingCategory)s. Please use only 5 in your post and category line.", + "are_you_sure_you_want_to_clear_this_form": + "Are you sure you want to clear this form?", + "uploading": "Uploading", + "draft_saved": "Draft saved.", + "editor": "Editor", + "insert_images_by_dragging_dropping": + "Insert images by dragging & dropping, ", + "pasting_from_the_clipboard": "pasting from the clipboard, ", + "selecting_them": "selecting them", + "image_upload": "Image upload", + "power_up_100": "Power Up 100%%", + "default_50_50": "Default (50%% / 50%%)", + "decline_payout": "Decline Payout", + "check_this_to_auto_upvote_your_post": + "Check this to auto-upvote your post", + "markdown_styling_guide": "Markdown Styling Guide", + "or_by": "or by", + "title": "Title", + "update_post": "Update Post", + "markdown_not_supported": "Markdown is not supported here" + }, + "category_selector_jsx": { + "tag_your_story": + "Tag (up to 5 tags), the first tag is your main category.", + "select_a_tag": "Select a tag", + "maximum_tag_length_is_24_characters": + "Maximum tag length is 24 characters", + "use_limited_amount_of_categories": + "Please use only %(amount)s categories", + "use_only_lowercase_letters": "Use only lowercase letters", + "use_one_dash": "Use only one dash", + "use_spaces_to_separate_tags": "Use spaces to separate tags", + "use_only_allowed_characters": + "Use only lowercase letters, digits and one dash", + "must_start_with_a_letter": "Must start with a letter", + "must_end_with_a_letter_or_number": "Must end with a letter or number" + }, + "postfull_jsx": { + "this_post_is_not_available_due_to_a_copyright_claim": + "This post is not available due to a copyright claim.", + "share_on_facebook": "Share on Facebook", + "share_on_twitter": "Share on Twitter", + "share_on_linkedin": "Share on Linkedin", + "recent_password": "Recent Password", + "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": + "In 3.5 days, convert %(amount)s %(DEBT_TOKEN)s into %(LIQUID_TOKEN)s", + "view_the_full_context": "View the full context", + "view_the_direct_parent": "View the direct parent", + "you_are_viewing_a_single_comments_thread_from": + "You are viewing a single comment's thread from" + }, + "market_jsx": { + "action": "Action", + "date_created": "Date Created", + "last_price": "Last price", + "24h_volume": "24h volume", + "spread": "Spread", + "total": "Total", + "available": "Available", + "lowest_ask": "Lowest ask", + "highest_bid": "Highest bid", + "buy_orders": "Buy Orders", + "sell_orders": "Sell Orders", + "trade_history": "Trade History", + "open_orders": "Open Orders", + "sell_amount_for_atleast": + "Sell %(amount_to_sell)s for at least %(min_to_receive)s (%(effectivePrice)s)", + "buy_atleast_amount_for": + "Buy at least %(min_to_receive)s for %(amount_to_sell)s (%(effectivePrice)s)", + "price_warning_above": + "This price is well above the current market price of %(marketPrice)s, are you sure?", + "price_warning_below": + "This price is well below the current market price of %(marketPrice)s, are you sure?", + "order_cancel_confirm": "Cancel order %(order_id)s from %(user)s?", + "order_cancelled": "Order %(order_id)s cancelled.", + "higher": "Higher", + "lower": "Lower", + "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": + "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" + }, + "recoveraccountstep1_jsx": { + "begin_recovery": "Begin Recovery", + "not_valid": "Not valid", + "account_name_is_not_found": "Account name is not found", + "unable_to_recover_account_not_change_ownership_recently": + "We are unable to recover this account, it has not changed ownership recently.", + "password_not_used_in_last_days": + "This password was not used on this account in the last 30 days.", + "request_already_submitted_contact_support": + "Your request has been already submitted and we are working on it. Please contact %(SUPPORT_EMAIL)s for the status of your request.", + "recover_account_intro": + "From time to time, a Steemian's owner key may be compromised. Stolen Account Recovery gives the rightful account owner 30 days to recover their account from the moment the thief changed their owner key. Stolen Account Recovery can only be used on %(APP_URL)s if the account owner had previously listed '%(APP_NAME)s' as their account trustee and complied with %(APP_NAME)s's Terms of Service.", + "login_with_facebook_or_reddit_media_to_verify_identity": + "Please login with Facebook or Reddit to verify your identity", + "login_with_social_media_to_verify_identity": + "Please login with %(provider)s to verify your identity", + "enter_email_toverify_identity": + "We need to verify your identity. Please enter your email address below to begin the verification.", + "continue_with_email": "Continue with Email", + "thanks_for_submitting_request_for_account_recovery": + "Thanks for submitting your request for Account Recovery using %(APP_NAME)s's blockchain-based multi factor authentication. We will respond to you as quickly as possible, however, please expect there may be some delay in response due to high volume of emails. Please be prepared to verify your identity.", + "recovering_account": "Recovering account", + "recover_account": "Recover Account", + "checking_account_owner": "Checking account owner", + "sending_recovery_request": "Sending recovery request", + "cant_confirm_account_ownership": + "We can't confirm account ownership. Check your password", + "account_recovery_request_not_confirmed": + "Account recovery request is not confirmed yet, please get back later, thank you for your patience." + }, + "user_profile": { + "unknown_account": "Unknown Account", + "user_hasnt_made_any_posts_yet": + "Looks like %(name)s hasn't made any posts yet!", + "user_hasnt_started_bloggin_yet": + "Looks like %(name)s hasn't started blogging yet!", + "user_hasnt_followed_anything_yet": + "Looks like %(name)s might not be following anyone yet! If %(name)s recently added new users to follow, their personalized feed will populate once new content is available.", + "user_hasnt_had_any_replies_yet": "%(name)s hasn't had any replies yet", + "looks_like_you_havent_posted_anything_yet": + "Looks like you haven't posted anything yet.", + "create_a_post": "Create a Post", + "explore_trending_articles": "Explore Trending Articles", + "read_the_quick_start_guide": "Read The Quick Start Guide", + "browse_the_faq": "Browse The FAQ", + "followers": "Followers", + "this_is_users_reputations_score_it_is_based_on_history_of_votes": + "This is %(name)s's reputation score.\n\nThe reputation score is based on the history of votes received by the account, and is used to hide low quality content.", + "follower_count": { + "zero": "No followers", + "one": "1 follower", + "other": "%(count)s followers" + }, + "followed_count": { + "zero": "Not following anybody", + "one": "1 following", + "other": "%(count)s following" + }, + "post_count": { + "zero": "No posts", + "one": "1 post", + "other": "%(count)s posts" + } + }, + "authorrewards_jsx": { + "estimated_author_rewards_last_week": + "Estimated author rewards last week", + "author_rewards_history": "Author Rewards History" + }, + "curationrewards_jsx": { + "estimated_curation_rewards_last_week": + "Estimated curation rewards last week", + "curation_rewards_history": "Curation Rewards History" + }, + "post_jsx": { + "now_showing_comments_with_low_ratings": + "Now showing comments with low ratings", + "sort_order": "Sort Order", + "comments_were_hidden_due_to_low_ratings": + "Comments were hidden due to low ratings" + }, + "voting_jsx": { + "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": + "Flagging a post can remove rewards and make this material less visible. Some common reasons to flag", + "disagreement_on_rewards": "Disagreement on rewards", + "fraud_or_plagiarism": "Fraud or Plagiarism", + "hate_speech_or_internet_trolling": "Hate Speech or Internet Trolling", + "intentional_miss_categorized_content_or_spam": + "Intentional miss-categorized content or Spam", + "pending_payout": "Pending Payout $%(value)s", + "payout_declined": "Payout Declined", + "max_accepted_payout": "Max Accepted Payout $%(value)s", + "promotion_cost": "Promotion Cost $%(value)s", + "past_payouts": "Past Payouts $%(value)s", + "past_payouts_author": " - Author $%(value)s", + "past_payouts_curators": " - Curators $%(value)s", + "we_will_reset_curation_rewards_for_this_post": + "will reset your curation rewards for this post", + "removing_your_vote": "Removing your vote", + "changing_to_an_upvote": "Changing to an Up-Vote", + "changing_to_a_downvote": "Changing to a Down-Vote", + "confirm_flag": "Confirm Flag", + "and_more": "and %(count)s more", + "votes_plural": { + "one": "%(count)s vote", + "other": "%(count)s votes" + } + }, + "witnesses_jsx": { + "witness_thread": "witness thread", + "top_witnesses": "Witness Voting", + "you_have_votes_remaining": { + "zero": "You have no votes remaining", + "one": "You have 1 vote remaining", + "other": "You have %(count)s votes remaining" + }, + "you_can_vote_for_maximum_of_witnesses": + "You can vote for a maximum of 30 witnesses", + "witness": "Witness", + "information": "Information", + "if_you_want_to_vote_outside_of_top_enter_account_name": + "If you would like to vote for a witness outside of the top 50, enter the account name below to cast a vote", + "set_witness_proxy": + "You can also choose a proxy that will vote for witnesses for you. This will reset your current witness selection.", + "witness_set": + "You have set a voting proxy. If you would like to re-enable manual voting, please clear your proxy.", + "witness_proxy_current": "Your current proxy is", + "witness_proxy_set": "Set proxy", + "witness_proxy_clear": "Clear proxy", + "proxy_update_error": "Your proxy was not updated" + }, + "votesandcomments_jsx": { + "no_responses_yet_click_to_respond": + "No responses yet. Click to respond.", + "response_count_tooltip": { + "zero": "no responses. Click to respond.", + "one": "1 response. Click to respond.", + "other": "%(count)s responses. Click to respond." + }, + "vote_count": { + "zero": "no votes", + "one": "1 vote", + "other": "%(count)s votes" + } + }, + "userkeys_jsx": { + "public": "Public", + "private": "Private", + "public_something_key": "Public %(key)s Key", + "private_something_key": "Private %(key)s Key", + "posting_key_is_required_it_should_be_different": + "The posting key is used for posting and voting. It should be different from the active and owner keys.", + "the_active_key_is_used_to_make_transfers_and_place_orders": + "The active key is used to make transfers and place orders in the internal market.", + "the_owner_key_is_required_to_change_other_keys": + "The owner key is the master key for the account and is required to change the other keys.", + "the_private_key_or_password_should_be_kept_offline": + "The private key or password for the owner key should be kept offline as much as possible.", + "the_memo_key_is_used_to_create_and_read_memos": + "The memo key is used to create and read memos." + }, + "suggestpassword_jsx": { + "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": + "%(APP_NAME)s cannot recover passwords. Keep this page in a secure location, such as a fireproof safe or safety deposit box.", + "APP_NAME_password_backup": "%(APP_NAME)s Password Backup", + "APP_NAME_password_backup_required": + "%(APP_NAME)s Password Backup (required)", + "after_printing_write_down_your_user_name": + "After printing, write down your user name" + }, + "converttosteem_jsx": { + "your_existing_DEBT_TOKEN_are_liquid_and_transferable": + "Your existing %(DEBT_TOKEN)s are liquid and transferable. Instead you may wish to trade %(DEBT_TOKEN)s directly in this site under %(link)s or transfer to an external market.", + "this_is_a_price_feed_conversion": + "This is a price feed conversion. The 3.5 day delay is necessary to prevent abuse from gaming the price feed average", + "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", + "DEBT_TOKEN_will_be_unavailable": + "This action will take place 3.5 days from now and can not be canceled. These %(DEBT_TOKEN)s will immediately become unavailable" + }, + "tips_js": { + "liquid_token": + "Tradeable tokens that may be transferred anywhere at anytime.
        %(LIQUID_TOKEN)s can be converted to %(VESTING_TOKEN)s in a process called powering up.", + "influence_token": + "Influence tokens which give you more control over post payouts and allow you to earn on curation rewards.", + "estimated_value": + "The estimated value is based on an average value of %(LIQUID_TOKEN)s in US dollars.", + "non_transferable": + "%(VESTING_TOKEN)s is non-transferable and requires 3 months (13 payments) to convert back to %(LIQUID_TOKEN)s.", + "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": + "Converted %(VESTING_TOKEN)s can be sent to yourself or someone else but can not transfer again without converting back to %(LIQUID_TOKEN)s.", + "part_of_your_steem_power_is_currently_delegated": + "Part of your STEEM POWER is currently delegated to you. Delegation is donated for influence or to help new users perform actions on steemit. Your delegation amount can fluctuate." + }, + "promote_post_jsx": { + "promote_post": "Promote Post", + "spend_your_DEBT_TOKEN_to_advertise_this_post": + "Spend your %(DEBT_TOKEN)s's to advertise this post in the promoted content section", + "you_successfully_promoted_this_post": + "You successfully promoted this post", + "this_post_was_hidden_due_to_low_ratings": + "This post was hidden due to low ratings" + }, + "about_jsx": { + "about_app": "About %(APP_NAME)s", + "about_app_details": + "%(APP_NAME)s is a social media platform where everyone gets paid for creating and curating content. It leverages a robust digital points system, called Steem, that supports real value for digital rewards through market price discovery and liquidity.", + "learn_more_at_app_url": "Learn more at %(APP_URL)s", + "resources": "Resources" + }, + "markdownviewer_jsx": { + "images_were_hidden_due_to_low_ratings": + "Images were hidden due to low ratings." + }, + "postsummary_jsx": { + "resteemed": "resteemed", + "resteemed_by": "Resteemed by", + "reveal_it": "Reveal this post", + "adjust_your": "adjust your", + "display_preferences": "display preferences", + "create_an_account": "create an account", + "to_save_your_preferences": "to save your preferences" + }, + "posts_index": { + "empty_feed_1": "Looks like you haven't followed anything yet", + "empty_feed_2": + "If you recently added new users to follow, your personalized feed will populate once new content is available", + "empty_feed_3": "Explore Trending Articles", + "empty_feed_4": "Read The Quick Start Guide", + "empty_feed_5": "Browse The FAQ" + }, + "transferhistoryrow_jsx": { + "to_savings": "to savings", + "from_savings": "from savings", + "cancel_transfer_from_savings": "Cancel transfer from savings", + "stop_power_down": "Stop power down", + "start_power_down_of": "Start power down of", + "receive_interest_of": "Receive interest of" + }, + "savingswithdrawhistory_jsx": { + "cancel_this_withdraw_request": "Cancel this withdraw request?", + "pending_savings_withdrawals": "PENDING SAVINGS WITHDRAWS", + "withdraw": "Withdraw %(amount)s", + "to": "to %(to)s", + "from_to": "from %(from)s to %(to)s" + }, + "explorepost_jsx": { + "copied": "Copied!", + "copy": "COPY", + "alternative_sources": "Alternative Sources" + }, + "header_jsx": { + "home": "home", + "create_a_post": "Create a post", + "change_account_password": "Change Account Password", + "create_account": "Create Account", + "stolen_account_recovery": "Stolen Account Recovery", + "people_following": "People following", + "people_followed_by": "People followed by", + "curation_rewards_by": "Curation rewards by", + "author_rewards_by": "Author rewards by", + "replies_to": "Replies to", + "comments_by": "Comments by" + }, + "loginform_jsx": { + "you_need_a_private_password_or_key": + "You need a private password or key (not a public key)", + "cryptography_test_failed": "Cryptography test failed", + "unable_to_log_you_in": + "We will be unable to log you in with this browser.", + "the_latest_versions_of": "The latest versions of ", + "are_well_tested_and_known_to_work_with": + "are well tested and known to work with %(APP_URL)s.", + "due_to_server_maintenance": + "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", + "login_to_vote": "Login to Vote", + "login_to_post": "Login to Post", + "login_to_comment": "Login to Comment", + "posting": "Posting", + "active_or_owner": "Active or Owner", + "this_password_is_bound_to_your_account_owner_key": + "This password is bound to your account's owner key and can not be used to login to this site.", + "however_you_can_use_it_to": "However, you can use it to ", + "update_your_password": "update your password", + "to_obtain_a_more_secure_set_of_keys": + "to obtain a more secure set of keys.", + "this_password_is_bound_to_your_account_active_key": + "This password is bound to your account's active key and can not be used to login to this page.", + "you_may_use_this_active_key_on_other_more": + "You may use this active key on other more secure pages like the Wallet or Market pages.", + "you_account_has_been_successfully_created": + "You account has been successfully created!", + "you_account_has_been_successfully_recovered": + "You account has been successfully recovered!", + "password_update_succes": + "The password for %(accountName)s was successfully updated", + "password_info": + "This password or private key was entered incorrectly. There is probably a handwriting or data-entry error. Hint: A password or private key generated by Steemit will never contain 0 (zero), O (capital o), I (capital i) and l (lower case L) characters.", + "enter_your_username": "Enter your username", + "password_or_wif": "Password or WIF", + "this_operation_requires_your_key_or_master_password": + "This operation requires your %(authType)s key or Master password.", + "keep_me_logged_in": "Keep me logged in", + "amazing_community": "amazing community", + "to_comment_and_reward_others": " to comment and reward others.", + "signup_button": "Sign up now to earn ", + "signup_button_emphasis": "FREE STEEM!", + "sign_up_get_steem": "Sign up. Get STEEM", + "returning_users": "Returning Users: ", + "join_our": "Join our" + }, + "chainvalidation_js": { + "account_name_should": "Account name should ", + "not_be_empty": "not be empty.", + "be_longer": "be longer.", + "be_shorter": "be shorter.", + "each_account_segment_should": "Each account segment should ", + "start_with_a_letter": "start with a letter.", + "have_only_letters_digits_or_dashes": + "have only letters, digits, or dashes.", + "have_only_one_dash_in_a_row": "have only one dash in a row.", + "end_with_a_letter_or_digit": "end with a letter or digit.", + "verified_exchange_no_memo": + "You must include a memo for your exchange transfer." + }, + "settings_jsx": { + "invalid_url": "Invalid URL", + "name_is_too_long": "Name is too long", + "name_must_not_begin_with": "Name must not begin with @", + "about_is_too_long": "About is too long", + "location_is_too_long": "Location is too long", + "website_url_is_too_long": "Website URL is too long", + "public_profile_settings": "Public Profile Settings", + "private_post_display_settings": "Private Post Display Settings", + "not_safe_for_work_nsfw_content": "Not safe for work (NSFW) content", + "always_hide": "Always hide", + "always_warn": "Always warn", + "always_show": "Always show", + "muted_users": "Muted Users", + "update": "Update", + "profile_image_url": "Profile picture url", + "cover_image_url": "Cover image url", + "profile_name": "Display Name", + "profile_about": "About", + "profile_location": "Location", + "profile_website": "Website" + }, + "transfer_jsx": { + "amount_is_in_form": "Amount is in the form 99999.999", + "insufficient_funds": "Insufficient funds", + "use_only_3_digits_of_precison": "Use only 3 digits of precison", + "send_to_account": "Send to account", + "asset": "Asset", + "this_memo_is_private": "This memo is private", + "this_memo_is_public": "This memo is public", + "convert_to_VESTING_TOKEN": "Convert to %(VESTING_TOKEN)s", + "balance_subject_to_3_day_withdraw_waiting_period": + "Balance subject to 3 day withdraw waiting period,", + "move_funds_to_another_account": + "Move funds to another %(APP_NAME)s account.", + "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": + "Protect funds by requiring a 3 day withdraw waiting period.", + "withdraw_funds_after_the_required_3_day_waiting_period": + "Withdraw funds after the required 3 day waiting period.", + "from": "From", + "to": "To", + "asset_currently_collecting": + "%(asset)s currently collecting %(interest)s%% APR.", + "beware_of_spam_and_phishing_links": + "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." + }, + "userwallet_jsx": { + "conversion_complete_tip": "Will complete on", + "in_conversion": "%(amount)s in conversion", + "transfer_to_savings": "Transfer to Savings", + "power_up": "Power Up", + "power_down": "Power Down", + "market": "Market", + "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", + "withdraw_LIQUID_TOKEN": "Withdraw %(LIQUID_TOKEN)s", + "withdraw_DEBT_TOKENS": "Withdraw %(DEBT_TOKENS)s", + "tokens_worth_about_1_of_LIQUID_TICKER": + "Tokens worth about $1.00 of %(LIQUID_TICKER)s, currently collecting %(sbdInterest)s%% APR.", + "savings": "SAVINGS", + "estimated_account_value": "Estimated Account Value", + "next_power_down_is_scheduled_to_happen": + "The next power down is scheduled to happen", + "transfers_are_temporary_disabled": "Transfers are temporary disabled.", + "history": "HISTORY", + "redeem_rewards": "Redeem Rewards (Transfer to Balance)", + "buy_steem_or_steem_power": "Buy STEEM or STEEM POWER" + }, + "powerdown_jsx": { + "power_down": "Power Down", + "amount": "Amount", + "already_power_down": + "You are already powering down %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s paid out so far). Note that if you change the power down amount the payout schedule will reset.", + "delegating": + "You are delegating %(AMOUNT)s %(LIQUID_TICKER)s. That amount is locked up and not available to power down until the delegation is removed and a full reward period has passed.", + "per_week": "That's ~%(AMOUNT)s %(LIQUID_TICKER)s per week.", + "warning": + "Leaving less than %(AMOUNT)s %(VESTING_TOKEN)s in your account is not recommended and can leave your account in a unusable state.", + "error": "Unable to power down (ERROR: %(MESSAGE)s)" + }, + "checkloginowner_jsx": { + "your_password_permissions_were_reduced": + "Your password permissions were reduced", + "if_you_did_not_make_this_change": + "If you did not make this change please", + "ownership_changed_on": "Ownership Changed On ", + "deadline_for_recovery_is": "Deadline for recovery is", + "i_understand_dont_show_again": "I understand, don't show me again" } - }, - "userkeys_jsx": { - "public": "Public", - "private": "Private", - "public_something_key": "Public %(key)s Key", - "private_something_key": "Private %(key)s Key", - "posting_key_is_required_it_should_be_different": "The posting key is used for posting and voting. It should be different from the active and owner keys.", - "the_active_key_is_used_to_make_transfers_and_place_orders": "The active key is used to make transfers and place orders in the internal market.", - "the_owner_key_is_required_to_change_other_keys": "The owner key is the master key for the account and is required to change the other keys.", - "the_private_key_or_password_should_be_kept_offline": "The private key or password for the owner key should be kept offline as much as possible.", - "the_memo_key_is_used_to_create_and_read_memos": "The memo key is used to create and read memos." - }, - "suggestpassword_jsx": { - "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": "%(APP_NAME)s cannot recover passwords. Keep this page in a secure location, such as a fireproof safe or safety deposit box.", - "APP_NAME_password_backup": "%(APP_NAME)s Password Backup", - "APP_NAME_password_backup_required": "%(APP_NAME)s Password Backup (required)", - "after_printing_write_down_your_user_name": "After printing, write down your user name" - }, - "converttosteem_jsx": { - "your_existing_DEBT_TOKEN_are_liquid_and_transferable": "Your existing %(DEBT_TOKEN)s are liquid and transferable. Instead you may wish to trade %(DEBT_TOKEN)s directly in this site under %(link)s or transfer to an external market.", - "this_is_a_price_feed_conversion": "This is a price feed conversion. The 3.5 day delay is necessary to prevent abuse from gaming the price feed average", - "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", - "DEBT_TOKEN_will_be_unavailable": "This action will take place 3.5 days from now and can not be canceled. These %(DEBT_TOKEN)s will immediately become unavailable" - }, - "tips_js": { - "liquid_token": "Tradeable tokens that may be transferred anywhere at anytime.
        %(LIQUID_TOKEN)s can be converted to %(VESTING_TOKEN)s in a process called powering up.", - "influence_token": "Influence tokens which give you more control over post payouts and allow you to earn on curation rewards.", - "estimated_value": "The estimated value is based on an average value of %(LIQUID_TOKEN)s in US dollars.", - "non_transferable": "%(VESTING_TOKEN)s is non-transferable and requires 3 months (13 payments) to convert back to %(LIQUID_TOKEN)s.", - "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": "Converted %(VESTING_TOKEN)s can be sent to yourself or someone else but can not transfer again without converting back to %(LIQUID_TOKEN)s.", - "part_of_your_steem_power_is_currently_delegated": "Part of your STEEM POWER is currently delegated to you. Delegation is donated for influence or to help new users perform actions on steemit. Your delegation amount can fluctuate." - }, - "promote_post_jsx": { - "promote_post": "Promote Post", - "spend_your_DEBT_TOKEN_to_advertise_this_post": "Spend your %(DEBT_TOKEN)s's to advertise this post in the promoted content section", - "you_successfully_promoted_this_post": "You successfully promoted this post", - "this_post_was_hidden_due_to_low_ratings": "This post was hidden due to low ratings" - }, - "about_jsx": { - "about_app": "About %(APP_NAME)s", - "about_app_details": "%(APP_NAME)s is a social media platform where everyone gets paid for creating and curating content. It leverages a robust digital points system, called Steem, that supports real value for digital rewards through market price discovery and liquidity.", - "learn_more_at_app_url": "Learn more at %(APP_URL)s", - "resources": "Resources" - }, - "markdownviewer_jsx": { - "images_were_hidden_due_to_low_ratings": "Images were hidden due to low ratings." - }, - "postsummary_jsx": { - "resteemed": "resteemed", - "resteemed_by": "Resteemed by", - "reveal_it": "Reveal this post", - "adjust_your": "adjust your", - "display_preferences": "display preferences", - "create_an_account": "create an account", - "to_save_your_preferences": "to save your preferences" - }, - "posts_index": { - "empty_feed_1": "Looks like you haven't followed anything yet", - "empty_feed_2": "If you recently added new users to follow, your personalized feed will populate once new content is available", - "empty_feed_3": "Explore Trending Articles", - "empty_feed_4": "Read The Quick Start Guide", - "empty_feed_5": "Browse The FAQ" - }, - "transferhistoryrow_jsx": { - "to_savings": "to savings", - "from_savings": "from savings", - "cancel_transfer_from_savings": "Cancel transfer from savings", - "stop_power_down": "Stop power down", - "start_power_down_of": "Start power down of", - "receive_interest_of": "Receive interest of" - }, - "savingswithdrawhistory_jsx": { - "cancel_this_withdraw_request": "Cancel this withdraw request?", - "pending_savings_withdrawals": "PENDING SAVINGS WITHDRAWS", - "withdraw": "Withdraw %(amount)s", - "to": "to %(to)s", - "from_to": "from %(from)s to %(to)s" - }, - "explorepost_jsx": { - "copied": "Copied!", - "copy": "COPY", - "alternative_sources": "Alternative Sources" - }, - "header_jsx": { - "home": "home", - "create_a_post": "Create a post", - "change_account_password": "Change Account Password", - "create_account": "Create Account", - "stolen_account_recovery": "Stolen Account Recovery", - "people_following": "People following", - "people_followed_by": "People followed by", - "curation_rewards_by": "Curation rewards by", - "author_rewards_by": "Author rewards by", - "replies_to": "Replies to", - "comments_by": "Comments by" - }, - "loginform_jsx": { - "you_need_a_private_password_or_key": "You need a private password or key (not a public key)", - "cryptography_test_failed": "Cryptography test failed", - "unable_to_log_you_in": "We will be unable to log you in with this browser.", - "the_latest_versions_of": "The latest versions of ", - "are_well_tested_and_known_to_work_with": "are well tested and known to work with %(APP_URL)s.", - "due_to_server_maintenance": "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", - "login_to_vote": "Login to Vote", - "login_to_post": "Login to Post", - "login_to_comment": "Login to Comment", - "posting": "Posting", - "active_or_owner": "Active or Owner", - "this_password_is_bound_to_your_account_owner_key": "This password is bound to your account's owner key and can not be used to login to this site.", - "however_you_can_use_it_to": "However, you can use it to ", - "update_your_password": "update your password", - "to_obtain_a_more_secure_set_of_keys": "to obtain a more secure set of keys.", - "this_password_is_bound_to_your_account_active_key": "This password is bound to your account's active key and can not be used to login to this page.", - "you_may_use_this_active_key_on_other_more": "You may use this active key on other more secure pages like the Wallet or Market pages.", - "you_account_has_been_successfully_created": "You account has been successfully created!", - "you_account_has_been_successfully_recovered": "You account has been successfully recovered!", - "password_update_succes": "The password for %(accountName)s was successfully updated", - "password_info": "This password or private key was entered incorrectly. There is probably a handwriting or data-entry error. Hint: A password or private key generated by Steemit will never contain 0 (zero), O (capital o), I (capital i) and l (lower case L) characters.", - "enter_your_username": "Enter your username", - "password_or_wif": "Password or WIF", - "this_operation_requires_your_key_or_master_password": "This operation requires your %(authType)s key or Master password.", - "keep_me_logged_in": "Keep me logged in", - "amazing_community": "amazing community", - "to_comment_and_reward_others": " to comment and reward others.", - "signup_button": "Sign up now to earn ", - "signup_button_emphasis": "FREE STEEM!", - "sign_up_get_steem": "Sign up. Get STEEM", - "returning_users": "Returning Users: ", - "join_our": "Join our" - }, - "chainvalidation_js": { - "account_name_should": "Account name should ", - "not_be_empty": "not be empty.", - "be_longer": "be longer.", - "be_shorter": "be shorter.", - "each_account_segment_should": "Each account segment should ", - "start_with_a_letter": "start with a letter.", - "have_only_letters_digits_or_dashes": "have only letters, digits, or dashes.", - "have_only_one_dash_in_a_row": "have only one dash in a row.", - "end_with_a_letter_or_digit": "end with a letter or digit.", - "verified_exchange_no_memo": "You must include a memo for your exchange transfer." - }, - "settings_jsx": { - "invalid_url": "Invalid URL", - "name_is_too_long": "Name is too long", - "name_must_not_begin_with": "Name must not begin with @", - "about_is_too_long": "About is too long", - "location_is_too_long": "Location is too long", - "website_url_is_too_long": "Website URL is too long", - "public_profile_settings": "Public Profile Settings", - "private_post_display_settings": "Private Post Display Settings", - "not_safe_for_work_nsfw_content": "Not safe for work (NSFW) content", - "always_hide": "Always hide", - "always_warn": "Always warn", - "always_show": "Always show", - "muted_users": "Muted Users", - "update": "Update", - "profile_image_url": "Profile picture url", - "cover_image_url": "Cover image url", - "profile_name": "Display Name", - "profile_about": "About", - "profile_location": "Location", - "profile_website": "Website" - }, - "transfer_jsx": { - "amount_is_in_form": "Amount is in the form 99999.999", - "insufficient_funds": "Insufficient funds", - "use_only_3_digits_of_precison": "Use only 3 digits of precison", - "send_to_account": "Send to account", - "asset": "Asset", - "this_memo_is_private": "This memo is private", - "this_memo_is_public": "This memo is public", - "convert_to_VESTING_TOKEN": "Convert to %(VESTING_TOKEN)s", - "balance_subject_to_3_day_withdraw_waiting_period": "Balance subject to 3 day withdraw waiting period,", - "move_funds_to_another_account": "Move funds to another %(APP_NAME)s account.", - "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": "Protect funds by requiring a 3 day withdraw waiting period.", - "withdraw_funds_after_the_required_3_day_waiting_period": "Withdraw funds after the required 3 day waiting period.", - "from": "From", - "to": "To", - "asset_currently_collecting": "%(asset)s currently collecting %(interest)s%% APR.", - "beware_of_spam_and_phishing_links": "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." - }, - "userwallet_jsx": { - "conversion_complete_tip": "Will complete on", - "in_conversion": "%(amount)s in conversion", - "transfer_to_savings": "Transfer to Savings", - "power_up": "Power Up", - "power_down": "Power Down", - "market": "Market", - "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", - "withdraw_LIQUID_TOKEN": "Withdraw %(LIQUID_TOKEN)s", - "withdraw_DEBT_TOKENS": "Withdraw %(DEBT_TOKENS)s", - "tokens_worth_about_1_of_LIQUID_TICKER": "Tokens worth about $1.00 of %(LIQUID_TICKER)s, currently collecting %(sbdInterest)s%% APR.", - "savings": "SAVINGS", - "estimated_account_value": "Estimated Account Value", - "next_power_down_is_scheduled_to_happen": "The next power down is scheduled to happen", - "transfers_are_temporary_disabled": "Transfers are temporary disabled.", - "history": "HISTORY", - "redeem_rewards": "Redeem Rewards (Transfer to Balance)", - "buy_steem_or_steem_power": "Buy STEEM or STEEM POWER" - }, - "powerdown_jsx": { - "power_down": "Power Down", - "amount": "Amount", - "already_power_down": "You are already powering down %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s paid out so far). Note that if you change the power down amount the payout schedule will reset.", - "delegating": "You are delegating %(AMOUNT)s %(LIQUID_TICKER)s. That amount is locked up and not available to power down until the delegation is removed and a full reward period has passed.", - "per_week": "That's ~%(AMOUNT)s %(LIQUID_TICKER)s per week.", - "warning": "Leaving less than %(AMOUNT)s %(VESTING_TOKEN)s in your account is not recommended and can leave your account in a unusable state.", - "error": "Unable to power down (ERROR: %(MESSAGE)s)" - }, - "checkloginowner_jsx": { - "your_password_permissions_were_reduced": "Your password permissions were reduced", - "if_you_did_not_make_this_change": "If you did not make this change please", - "ownership_changed_on": "Ownership Changed On ", - "deadline_for_recovery_is": "Deadline for recovery is", - "i_understand_dont_show_again": "I understand, don't show me again" - } } diff --git a/src/app/locales/es.json b/src/app/locales/es.json index 1de58434f..3d06931ed 100644 --- a/src/app/locales/es.json +++ b/src/app/locales/es.json @@ -1,649 +1,779 @@ { - "g": { - "age": "edad", - "amount": "Cantidad", - "and": "y", - "are_you_sure": "¿Estás seguro?", - "ask": "Preguntar", - "balance": "Saldo", - "balances": "Saldos", - "bid": "Oferta", - "blog": "Blog", - "browse": "Navegar", - "buy": "Comprar", - "buy_or_sell": "Comprar o Vender", - "by": "por", - "cancel": "Cancelar", - "change_password": "Cambiar Contraseña", - "choose_language": "Elegir Idioma", - "clear": "Limpiar", - "close": "Cerrar", - "collapse_or_expand": "Plegar/Expandir", - "comments": "Comentarios", - "confirm": "Confirmar", - "convert": "Convertir", - "date": "Fecha", - "delete": "Borrar", - "dismiss": "Descartar", - "edit": "Editar", - "email": "Email", - "feed": "Favoritos", - "follow": "Seguir", - "for": "para", - "from": "de", - "go_back": "Volver", - "hide": "Ocultar", - "in": "en", - "in_reply_to": "en respuesta a", - "insufficient_balance": "Saldo insuficiente", - "invalid_amount": "Cantidad inválida", - "joined": "Unió", - "loading": "Cargando", - "login": "Iniciar sesión", - "logout": "Cerrar sesión", - "memo": "Memo", - "mute": "Silenciar", - "new": "nuevo", - "newer": "Nuevo", - "next": "Próximo", - "no": "No", - "ok": "Ok", - "older": "Más viejo", - "or": "o", - "order_placed": "Órden dispuesta", - "password": "Contraseña", - "payouts": "Pagos", - "permissions": "Permisos", - "phishy_message": "Enlace expuesto a texto sin formato; cuidado con un posible intento de phishing", - "post": "Publicación", - "post_as": "Publicar como", - "posts": "Publicaciones", - "powered_up_100": "Powered Up 100 %%", - "preview": "Vista previa", - "previous": "Anterior", - "price": "Precio", - "print": "Imprimir", - "promote": "Promocionar", - "promoted": "promocionado", - "re": "RE", - "re_to": "RE %(topic)s", - "recent_password": "Contraseña reciente", - "receive": "Recibir", - "remove": "Quitar", - "remove_vote": "Quitar voto", - "replied_to": "Respondido a %(account)s", - "replies": "Respuestas", - "reply": "Respuesta", - "reply_count": { - "zero": "Sin respuestas", - "one": "1 respuesta", - "other": "%(count)s respuestas" - }, - "reputation": "Reputación", - "reveal_comment": "Mostrar Comentario", - "request": "solicitud", - "required": "Requerido", - "rewards": "Recompensa", - "save": "Guardar", - "saved": "Guardado", - "search": "Buscar", - "sell": "Vender", - "settings": "Configuración", - "share_this_post": "Compartir esta publicación", - "show": "Mostrar", - "sign_in": "Registrarse", - "sign_up": "Inscribirse", - "since": "desde", - "submit": "Enviar", - "power_up": "Power Up", - "submit_a_story": "Publicar", - "tag": "Etiqueta", - "to": "hasta", - "all_tags": "All tags", - "transfer": "Transferir", - "trending_topics": "Temas Tendencia", - "type": "Tipo", - "unfollow": "Dejar de seguir", - "unmute": "Desmutear", - "unknown": "Desconocido", - "upvote": "Votar", - "upvote_post": "Votar publicación", - "username": "Nombre de usuario", - "version": "Versión", - "vote": "Voto", - "votes": "votos", - "wallet": "Monedero", - "warning": "advertencia", - "yes": "Sí", - "posting": "Publicando", - "owner": "Propietario", - "active": "Activo", - "account_not_found": "Cuenta no encontrada", - "this_is_wrong_password": "Esta es la contraseña errónea", - "do_you_need_to": "Necesitas", - "account_name": "Nombre de cuenta", - "recover_your_account": "recuperar tu cuenta", - "reset_usernames_password": "Restablecer la contraseña de %(username)s", - "this_will_update_usernames_authtype_key": "Esto actualizará la clave %(authType)s de %(username)s", - "passwords_do_not_match": "Las contraseñas no coinciden", - "you_need_private_password_or_key_not_a_public_key": "Necesitas una contraseña o clave privada (no una clave pública)", - "the_rules_of_APP_NAME": { - "one": "La primera regla de %(APP_NAME)ses: No pierdas tu contraseña.", - "second": "La segunda regla de %(APP_NAME)s es: No pierdas tu contraseña.", - "third": "La tercera regla de %(APP_NAME)s es: No podemos recuperar tu contraseña.", - "fourth": "La cuarta regla: Si puedes recordar tu contraseña, esta no es segura.", - "fifth": "La quinta regla: Usa sólo contraseñas creadas aleatoriamente.", - "sixth": "La sexta regla: No le digas a nadie tu contraseña.", - "seventh": "La séptima regla: Haz siempre una copia de seguridad de tu contraseña." - }, - "recover_password": "Recuperar Cuenta", - "current_password": "Contraseña Actual", - "generated_password": "Contraseña Generada", - "backup_password_by_storing_it": "Haz una copia de seguridad guardándolo en tu monedero de contraseñas o en un archivo de texto", - "enter_account_show_password": "Introduce un nombre de cuenta correcto para mostrar la contraseña", - "click_to_generate_password": "Confirmar Contraseña", - "re_enter_generate_password": "Reintroduce la contraseña generada", - "understand_that_APP_NAME_cannot_recover_password": "Entiendo que (nombre APP) no puede recuperar contraseñas perdidas", - "i_saved_password": "He guardado con seguridad mi contraseña generada", - "update_password": "Actualizar Contraseña", - "confirm_password": "Confirmar Contraseña", - "account_updated": "Cuenta Actualizada", - "password_must_be_characters_or_more": "La contraseña tiene que tener %(amount)s caracteres o más", - "need_password_or_key": "Necesitas una contraseña o clave privada (no una clave pública)", - "login_to_see_memo": "accede para ver el memo", - "new_password": "Nueva Contraseña", - "incorrect_password": "Contraseña Incorrecta", - "username_does_not_exist": "El nombre de usuario no existe", - "account_name_should_start_with_a_letter": "El nombre de usuario debería empezar con una letra.", - "account_name_should_be_shorter": "El nombre de usuario debería ser más corto.", - "account_name_should_be_longer": "El nombre de usuario debería ser más largo.", - "account_name_should_have_only_letters_digits_or_dashes": "El nombre de usuario debería tener sólo letras, dígitos o guiones.", - "cannot_increase_reward_of_post_within_the_last_minute_before_payout": "No se puede aumentar la recompensa de la publicación dentro del último minuto antes del pago.", - "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": "Este voto ya existe, el usuario tiene que quitar el voto al witness", - "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": "Sólo una cuenta Steem permitida por dirección IP cada 10 minutos", - "resteem_this_post": "Reesteemea esta Publicación", - "reblog": "Reesteemear", - "write_your_story": "Escribe tu historia", - "remember_voting_and_posting_key": "Recuerda la clave de voto y de publicación", - "auto_login_question_mark": "¿Acceso automático?", - "hide_private_key": "Ocultar clave privada", - "show_private_key": "Mostrar clave privada", - "login_to_show": "Acceder para mostrar", - "not_valid_email": "Correo electrónico no válido", - "thank_you_for_being_an_early_visitor_to_APP_NAME": "Gracias por ser uno de los primeros en entrar en %(APP_NAME)s. Nos pondremos en contacto tan pronto como sea posible", - "author_rewards": "Recompensas de autor", - "curation_rewards": "Recompensas de curación", - "sorry_your_reddit_account_doesnt_have_enough_karma": "Lo sentimos pero tu cuenta de Reddit no tiene suficiente Karma para un registro gratis. Por favor, añade tu email para entrar en la lista de espera", - "register_with_facebook": "Regístrate con Facebook", - "or_click_the_button_below_to_register_with_facebook": "O haz click en el botón de abajo para registrarte con Facebook", - "server_returned_error": "el servidor devolvió el error", - "APP_NAME_support": "%(APP_NAME)s Soporte", - "please_email_questions_to": "Por favor, envía un mail con tus dudas a", - "next_7_strings_single_block": { - "authors_get_paid_when_people_like_you_upvote_their_post": "Autores cobran cuando gente como tú vota por sus posts", - "if_you_enjoyed_what_you_read_earn_amount": "Si te ha gustado lo que has leído aquí, crea tu cuenta hoy mismo y empieza a ganar STEEM!", - "free_steem": "¡STEEM GRATIS!", - "sign_up_earn_steem": "Regístrate ahora para ganar" - }, - "next_3_strings_together": { - "show_more": "Mostrar más", - "show_less": "Mostrar menos", - "value_posts": "Posts de bajo valor" - }, - "read_only_mode": "Debido a tareas de mantenimiento de los servidores, solo el modo lectura está disponible. Perdonen las molestias", - "tags_and_topics": "Etiquetas y Temas", - "show_more_topics": "Mostrar más temas", - "basic": "Básico", - "advanced": "Avanzado", - "views": { - "zero": "Ninguna visualización", - "one": "%(count)s Visualización", - "other": "%(count)s Visualizaciones" - }, - "responses": { - "zero": "Sin Respuestas", - "one": "%(count)s Respuesta", - "other": "%(count)s Respuestas" - }, - "post_key_warning": { - "confirm": "Está a punto de publicar una clave privada STEEM o una contraseña maestra. Probablemente perderá el control de la cuenta asociada y todos sus fondos.", - "warning": "Los usuarios legítimos, incluidos los empleados de Steemit Inc., nunca le pedirán una clave privada o contraseña maestra.", - "checkbox": "Entiendo" - } - }, - "navigation": { - "about": "Sobre", - "explore": "Explorar", - "APP_NAME_whitepaper": "%(APP_NAME)s Documentación técnica", - "buy_LIQUID_TOKEN": "Comprar %(LIQUID_TOKEN)s", - "sell_LIQUID_TOKEN": "Vender %(LIQUID_TOKEN)s", - "currency_market": "Mercado de monedas", - "stolen_account_recovery": "Recuperación de cuentas robadas", - "change_account_password": "Cambia la contraseña de la cuenta", - "witnesses": "Testigos", - "vote_for_witnesses": "Vota a los testigos", - "privacy_policy": "Política de Privacidad", - "terms_of_service": "Términos del Servicio", - "sign_up": "Unirse", - "learn_more": "Para saber más", - "welcome": "Bienvenido", - "faq": "Preguntas Frecuentes", - "shop": "The Steemit Shop", - "chat": "Chat de Steemit", - "app_center": "Centro de Aplicaciones Steemit", - "api_docs": "Documentos API de Steemit", - "bluepaper": "Steem Bluepaper", - "smt_whitepaper": "Libro blanco de SMT", - "whitepaper": "Libro blanco de Steem", - "intro_tagline": "El dinero habla.", - "intro_paragraph": "Tu voz tiene valor. Únete a la comunidad que te paga por publicar y votar contenido de alta calidad" - }, - "main_menu": { - "hot": "en alza", - "trending": "tendencia" - }, - "reply_editor": { - "shorten_title": "Abreviar el título", - "exceeds_maximum_length": "Excede la capacidad máxima (%(maxKb)sKB)", - "including_the_category": "(incluyendo categoría '%(rootCategory)s')", - "use_limited_amount_of_tags": "Tienes %(tagsLength)s tags totales %(includingCategory)s. Por favor usa solo 5 tags en tu post y en la línea de categorías", - "are_you_sure_you_want_to_clear_this_form": "¿Estás seguro de que quieres limpiar este formulario?", - "uploading": "Subiendo", - "draft_saved": "Borrador guardado.", - "editor": "Editor", - "insert_images_by_dragging_dropping": "Inserta imágenes arrastrando y soltando,", - "pasting_from_the_clipboard": "pegando desde el portapapeles,", - "selecting_them": "Seleccionándolos", - "image_upload": "Subir imagen", - "power_up_100": "Power Up 100%%", - "default_50_50": "Por defecto (50 1%% / 50 1%%)", - "decline_payout": "Rechazar Pago", - "check_this_to_auto_upvote_your_post": "Seleciona para autovotarte el post", - "markdown_styling_guide": "Guía para el Markdown", - "or_by": "o por", - "title": "Título", - "update_post": "Actualizar Publicación", - "markdown_not_supported": "Markdown no soportado aquí" - }, - "category_selector_jsx": { - "tag_your_story": "Etiqueta (hasta 5 etiquetas), la primera etiqueta es tu principal categoría.", - "select_a_tag": "Selecciona una etiqueta", - "maximum_tag_length_is_24_characters": "La longitud máxima de la etiqueta es de 24 caracteres", - "use_limited_amount_of_categories": "Por favor usa sólo %(amount)s categorías", - "use_only_lowercase_letters": "Usa sólo letras minúsculas", - "use_one_dash": "Usa sólo un guión", - "use_spaces_to_separate_tags": "Usa espacios para separar las etiquetas", - "use_only_allowed_characters": "Usa sólo letras minúsculas, dígitos y un guión", - "must_start_with_a_letter": "Debe empezar con una letra", - "must_end_with_a_letter_or_number": "Debe terminar con una letra o número" - }, - "postfull_jsx": { - "this_post_is_not_available_due_to_a_copyright_claim": "Este post no está disponible por reclamación de derechos de autor.", - "share_on_facebook": "Compartir en Facebook", - "share_on_twitter": "Compartir en Twitter", - "share_on_linkedin": "Compartir en Linkedin", - "recent_password": "Contraseña reciente", - "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": "En 3.5 días, convertir 1 %(amount)s 2 %(DEBT_TOKEN)s a 3 %(LIQUID_TOKEN)s", - "view_the_full_context": "Ver todo el contexto", - "view_the_direct_parent": "Ver el comentario relacionado", - "you_are_viewing_a_single_comments_thread_from": "Estás viendo los comentarios relacionados de" - }, - "market_jsx": { - "action": "Acción", - "date_created": "Fecha de creación", - "last_price": "Último precio", - "24h_volume": "Volumen de 24 horas", - "spread": "Difundir", - "total": "Total", - "available": "Disponible", - "lowest_ask": "Oferta más baja", - "highest_bid": "Oferta más alta", - "buy_orders": "Comprar órdenes", - "sell_orders": "Vender órdenes", - "trade_history": "Historia de compra venta", - "open_orders": "Órdenes abiertas", - "sell_amount_for_atleast": "Vender %(amount_to_sell)s al menos por %(min_to_receive)s (%(effectivePrice)s)", - "buy_atleast_amount_for": "Comprar al menos %(min_to_receive)s for %(amount_to_sell)s (%(effectivePrice)s)", - "price_warning_above": "Este precio está por debajo del precio de mercado que es %(marketPrice)s, estás seguro?", - "price_warning_below": "Este precio está por debajo del precio de mercado que es %(marketPrice)s, estás seguro?", - "order_cancel_confirm": "Cancelar órden %(order_id)s de %(user)s?", - "order_cancelled": "Órden %(order_id)s cancelada", - "higher": "Más alta", - "lower": "Más baja", - "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" - }, - "recoveraccountstep1_jsx": { - "begin_recovery": "Empezar Recuperación", - "not_valid": "No es válido", - "account_name_is_not_found": "Nombre de usuario no encontrado", - "unable_to_recover_account_not_change_ownership_recently": "No podemos recuperar esta cuenta, esta no ha cambiado su propiedad recientemente.", - "password_not_used_in_last_days": "Esta contraseña no ha sido usada en esta cuenta en los últimos 30 días.", - "request_already_submitted_contact_support": "Tu petición ha sido enviada y estamos trabajando en ella. Por favor contacta %(SUPPORT_EMAIL)s para ver el estado de la misma.", - "recover_account_intro": "Puede ocurrir que la owner key de algún usuario se vea comprometida. La funcionalidad de recuperación de contraseñas robadas le da al propietario de la cuenta 30 días para recuperar su cuenta desde el mismo momento que el delincuente ha cambiado la owner key. Esta funcionalidad sólo puede ser usada en %(APP_URL)s si el propietario de la cuenta ha incluído %(APP_NAME)s como su cuenta de confianza y además a comulgado con las condiciones y términos de servicio de %(APP_NAME)s", - "login_with_facebook_or_reddit_media_to_verify_identity": "Por favor accede con Fabebook o Reddit para verificar tu identidad", - "login_with_social_media_to_verify_identity": "Por favor entra con %(provider)s para verificar tu identidad", - "enter_email_toverify_identity": "Necesitamos verificar tu identidad. Por favor introduce tu dirección de correo electrónico debajo para empezar la verificación.", - "continue_with_email": "Continuar con correo electrónico", - "thanks_for_submitting_request_for_account_recovery": "Gracias por enviar tu petición a nuestro sistema de recuperación de contraseñas usando %(APP_NAME)s basado en la autentificación multi factor. Tan pronto como sea posible recibirás una respuesta, sim embargo, puede que haya algún retraso debido al alto volumen de peticiones. Por favor, esté preparado para verificar su identidad.", - "recovering_account": "Recuperando cuenta", - "recover_account": "Recuperar Cuenta", - "checking_account_owner": "Comprobando propietario de la cuenta", - "sending_recovery_request": "Enviando solicitud de recuperación ", - "cant_confirm_account_ownership": "No podemos confirmar la propiedad de la cuenta. Comprueba tu contraseña", - "account_recovery_request_not_confirmed": "La solicitud de recuperación de cuenta no está confirmada todavía, por favor vuelve más tarde, gracias por tu paciencia." - }, - "user_profile": { - "unknown_account": "Cuenta desconocida", - "user_hasnt_made_any_posts_yet": "Parece que %(name)s aún no ha comenzado a postear", - "user_hasnt_started_bloggin_yet": "Parece que %(name)s aún no ha comenzado su blog", - "user_hasnt_followed_anything_yet": "Parece que %(name)s aún no está siguiendo a nadie. Si %(name)s acaba de añadir nuevas cuentas para seguir, el feed personalizado aparecerá cuando el nuevo contenido esté disponible.", - "user_hasnt_had_any_replies_yet": "%(name)s todavía no ha tenido ninguna respuesta", - "looks_like_you_havent_posted_anything_yet": "Looks like you haven't posted anything yet.", - "create_a_post": "Create a Post", - "explore_trending_articles": "Explore Trending Articles", - "read_the_quick_start_guide": "Read The Quick Start Guide", - "browse_the_faq": "Browse The FAQ", - "followers": "Seguidores", - "this_is_users_reputations_score_it_is_based_on_history_of_votes": "Esto es la reputación de %(name)s's. La reputación está basada en la historia de votos recibidos por la cuenta y se usa para esconder contenido de baja calidad.", - "follower_count": { - "zero": "Sin seguidores", - "one": "1 seguidor", - "other": "%(count)s seguidores" - }, - "followed_count": { - "zero": "No sigue a nadie", - "one": "1 siguiendo", - "other": "%(count)s siguiendo" - }, - "post_count": { - "zero": "Sin publicaciones", - "one": "1 publicación", - "other": "%(count)s posts" - } - }, - "authorrewards_jsx": { - "estimated_author_rewards_last_week": "Recompensas de autor estimadas la semana pasada", - "author_rewards_history": "Historial de recompensas de autor" - }, - "curationrewards_jsx": { - "estimated_curation_rewards_last_week": "Recompensas de curación aproximadas de la semana pasada", - "curation_rewards_history": "Historial de recompensas de curación" - }, - "post_jsx": { - "now_showing_comments_with_low_ratings": "Mostrando ahora comentarios con bajas calificaciones", - "sort_order": "Clase de órden", - "comments_were_hidden_due_to_low_ratings": "Los comentarios fueron ocultados debido a las bajas calificaciones" - }, - "voting_jsx": { - "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": "Flagear un posts puede hacer desaparecer las recompensas y hacer el contenido menos visible. Usar el sentido común para flagear", - "disagreement_on_rewards": "Desacuerdo en las recompensas", - "fraud_or_plagiarism": "Fraude o Plagio", - "hate_speech_or_internet_trolling": "Discurso de odio o trolear en internat", - "intentional_miss_categorized_content_or_spam": "Uso erróneo de los tags o Spam", - "pending_payout": "Pago pendiente $ %(value)s", - "payout_declined": "Pago rehusado", - "max_accepted_payout": "Máximo pago aceptado $ %(value)s", - "promotion_cost": "Coste de promoción $ %(value)s", - "past_payouts": "Pagos pasados $ %(value)s", - "past_payouts_author": "Autor $ %(value)s", - "past_payouts_curators": "Curador $ %(value)s", - "we_will_reset_curation_rewards_for_this_post": "Reseteará tus recompensas de curación para este post", - "removing_your_vote": "Quitando tu voto", - "changing_to_an_upvote": "Cambiando a Up-vote", - "changing_to_a_downvote": "Cambiando a Down-Vote", - "confirm_flag": "Confirmar Flag", - "and_more": "y %(count)s más", - "votes_plural": { - "one": "%(count)s voto", - "other": "%(count)s votos" - } - }, - "witnesses_jsx": { - "witness_thread": "tema testigo", - "top_witnesses": "Votación para testigo", - "you_have_votes_remaining": { - "zero": "No tienes votos restantes", - "one": "Tienes 1 voto restante", - "other": "Te quedan %(count)s votos disponibles" - }, - "you_can_vote_for_maximum_of_witnesses": "Puedes votar como máximo a 30 testigos", - "witness": "Testigos", - "information": "Información", - "if_you_want_to_vote_outside_of_top_enter_account_name": "SI quieres votar a un testigo que está fuera de los top 50, por favor introduce el nombre de la cuenta para votarle", - "set_witness_proxy": "También puedes elegir un proxy que votará a los testigos por ti. Esta actividad reseteará tu actual selección de witness", - "witness_set": "Has seleccionado un proxy de votación. Si quieres volver a habilitar el voto manual, por favor borra el proxy.", - "witness_proxy_current": "Tu proxy actual es", - "witness_proxy_set": "Crear proxy", - "witness_proxy_clear": "Borrar proxy", - "proxy_update_error": "Tu proxy no fue actualizado" - }, - "votesandcomments_jsx": { - "no_responses_yet_click_to_respond": "Todavía sin respuestas. Pincha para responder.", - "response_count_tooltip": { - "zero": "sin respuestas. Pincha para responder.", - "one": "1 respuesta. Pincha para responder.", - "other": "%(count)s respuestas. Pincha para responder." - }, - "vote_count": { - "zero": "sin votos", - "one": "1 voto", - "other": "%(count)s votos" + "g": { + "age": "edad", + "amount": "Cantidad", + "and": "y", + "are_you_sure": "¿Estás seguro?", + "ask": "Preguntar", + "balance": "Saldo", + "balances": "Saldos", + "bid": "Oferta", + "blog": "Blog", + "browse": "Navegar", + "buy": "Comprar", + "buy_or_sell": "Comprar o Vender", + "by": "por", + "cancel": "Cancelar", + "change_password": "Cambiar Contraseña", + "choose_language": "Elegir Idioma", + "clear": "Limpiar", + "close": "Cerrar", + "collapse_or_expand": "Plegar/Expandir", + "comments": "Comentarios", + "confirm": "Confirmar", + "convert": "Convertir", + "date": "Fecha", + "delete": "Borrar", + "dismiss": "Descartar", + "edit": "Editar", + "email": "Email", + "feed": "Favoritos", + "follow": "Seguir", + "for": "para", + "from": "de", + "go_back": "Volver", + "hide": "Ocultar", + "in": "en", + "in_reply_to": "en respuesta a", + "insufficient_balance": "Saldo insuficiente", + "invalid_amount": "Cantidad inválida", + "joined": "Unió", + "loading": "Cargando", + "login": "Iniciar sesión", + "logout": "Cerrar sesión", + "memo": "Memo", + "mute": "Silenciar", + "new": "nuevo", + "newer": "Nuevo", + "next": "Próximo", + "no": "No", + "ok": "Ok", + "older": "Más viejo", + "or": "o", + "order_placed": "Órden dispuesta", + "password": "Contraseña", + "payouts": "Pagos", + "permissions": "Permisos", + "phishy_message": + "Enlace expuesto a texto sin formato; cuidado con un posible intento de phishing", + "post": "Publicación", + "post_as": "Publicar como", + "posts": "Publicaciones", + "powered_up_100": "Powered Up 100 %%", + "preview": "Vista previa", + "previous": "Anterior", + "price": "Precio", + "print": "Imprimir", + "promote": "Promocionar", + "promoted": "promocionado", + "re": "RE", + "re_to": "RE %(topic)s", + "recent_password": "Contraseña reciente", + "receive": "Recibir", + "remove": "Quitar", + "remove_vote": "Quitar voto", + "replied_to": "Respondido a %(account)s", + "replies": "Respuestas", + "reply": "Respuesta", + "reply_count": { + "zero": "Sin respuestas", + "one": "1 respuesta", + "other": "%(count)s respuestas" + }, + "reputation": "Reputación", + "reveal_comment": "Mostrar Comentario", + "request": "solicitud", + "required": "Requerido", + "rewards": "Recompensa", + "save": "Guardar", + "saved": "Guardado", + "search": "Buscar", + "sell": "Vender", + "settings": "Configuración", + "share_this_post": "Compartir esta publicación", + "show": "Mostrar", + "sign_in": "Registrarse", + "sign_up": "Inscribirse", + "since": "desde", + "submit": "Enviar", + "power_up": "Power Up", + "submit_a_story": "Publicar", + "tag": "Etiqueta", + "to": "hasta", + "all_tags": "All tags", + "transfer": "Transferir", + "trending_topics": "Temas Tendencia", + "type": "Tipo", + "unfollow": "Dejar de seguir", + "unmute": "Desmutear", + "unknown": "Desconocido", + "upvote": "Votar", + "upvote_post": "Votar publicación", + "username": "Nombre de usuario", + "version": "Versión", + "vote": "Voto", + "votes": "votos", + "wallet": "Monedero", + "warning": "advertencia", + "yes": "Sí", + "posting": "Publicando", + "owner": "Propietario", + "active": "Activo", + "account_not_found": "Cuenta no encontrada", + "this_is_wrong_password": "Esta es la contraseña errónea", + "do_you_need_to": "Necesitas", + "account_name": "Nombre de cuenta", + "recover_your_account": "recuperar tu cuenta", + "reset_usernames_password": "Restablecer la contraseña de %(username)s", + "this_will_update_usernames_authtype_key": + "Esto actualizará la clave %(authType)s de %(username)s", + "passwords_do_not_match": "Las contraseñas no coinciden", + "you_need_private_password_or_key_not_a_public_key": + "Necesitas una contraseña o clave privada (no una clave pública)", + "the_rules_of_APP_NAME": { + "one": + "La primera regla de %(APP_NAME)ses: No pierdas tu contraseña.", + "second": + "La segunda regla de %(APP_NAME)s es: No pierdas tu contraseña.", + "third": + "La tercera regla de %(APP_NAME)s es: No podemos recuperar tu contraseña.", + "fourth": + "La cuarta regla: Si puedes recordar tu contraseña, esta no es segura.", + "fifth": + "La quinta regla: Usa sólo contraseñas creadas aleatoriamente.", + "sixth": "La sexta regla: No le digas a nadie tu contraseña.", + "seventh": + "La séptima regla: Haz siempre una copia de seguridad de tu contraseña." + }, + "recover_password": "Recuperar Cuenta", + "current_password": "Contraseña Actual", + "generated_password": "Contraseña Generada", + "backup_password_by_storing_it": + "Haz una copia de seguridad guardándolo en tu monedero de contraseñas o en un archivo de texto", + "enter_account_show_password": + "Introduce un nombre de cuenta correcto para mostrar la contraseña", + "click_to_generate_password": "Confirmar Contraseña", + "re_enter_generate_password": "Reintroduce la contraseña generada", + "understand_that_APP_NAME_cannot_recover_password": + "Entiendo que (nombre APP) no puede recuperar contraseñas perdidas", + "i_saved_password": "He guardado con seguridad mi contraseña generada", + "update_password": "Actualizar Contraseña", + "confirm_password": "Confirmar Contraseña", + "account_updated": "Cuenta Actualizada", + "password_must_be_characters_or_more": + "La contraseña tiene que tener %(amount)s caracteres o más", + "need_password_or_key": + "Necesitas una contraseña o clave privada (no una clave pública)", + "login_to_see_memo": "accede para ver el memo", + "new_password": "Nueva Contraseña", + "incorrect_password": "Contraseña Incorrecta", + "username_does_not_exist": "El nombre de usuario no existe", + "account_name_should_start_with_a_letter": + "El nombre de usuario debería empezar con una letra.", + "account_name_should_be_shorter": + "El nombre de usuario debería ser más corto.", + "account_name_should_be_longer": + "El nombre de usuario debería ser más largo.", + "account_name_should_have_only_letters_digits_or_dashes": + "El nombre de usuario debería tener sólo letras, dígitos o guiones.", + "cannot_increase_reward_of_post_within_the_last_minute_before_payout": + "No se puede aumentar la recompensa de la publicación dentro del último minuto antes del pago.", + "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": + "Este voto ya existe, el usuario tiene que quitar el voto al witness", + "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": + "Sólo una cuenta Steem permitida por dirección IP cada 10 minutos", + "resteem_this_post": "Reesteemea esta Publicación", + "reblog": "Reesteemear", + "write_your_story": "Escribe tu historia", + "remember_voting_and_posting_key": + "Recuerda la clave de voto y de publicación", + "auto_login_question_mark": "¿Acceso automático?", + "hide_private_key": "Ocultar clave privada", + "show_private_key": "Mostrar clave privada", + "login_to_show": "Acceder para mostrar", + "not_valid_email": "Correo electrónico no válido", + "thank_you_for_being_an_early_visitor_to_APP_NAME": + "Gracias por ser uno de los primeros en entrar en %(APP_NAME)s. Nos pondremos en contacto tan pronto como sea posible", + "author_rewards": "Recompensas de autor", + "curation_rewards": "Recompensas de curación", + "sorry_your_reddit_account_doesnt_have_enough_karma": + "Lo sentimos pero tu cuenta de Reddit no tiene suficiente Karma para un registro gratis. Por favor, añade tu email para entrar en la lista de espera", + "register_with_facebook": "Regístrate con Facebook", + "or_click_the_button_below_to_register_with_facebook": + "O haz click en el botón de abajo para registrarte con Facebook", + "server_returned_error": "el servidor devolvió el error", + "APP_NAME_support": "%(APP_NAME)s Soporte", + "please_email_questions_to": "Por favor, envía un mail con tus dudas a", + "next_7_strings_single_block": { + "authors_get_paid_when_people_like_you_upvote_their_post": + "Autores cobran cuando gente como tú vota por sus posts", + "if_you_enjoyed_what_you_read_earn_amount": + "Si te ha gustado lo que has leído aquí, crea tu cuenta hoy mismo y empieza a ganar STEEM!", + "free_steem": "¡STEEM GRATIS!", + "sign_up_earn_steem": "Regístrate ahora para ganar" + }, + "next_3_strings_together": { + "show_more": "Mostrar más", + "show_less": "Mostrar menos", + "value_posts": "Posts de bajo valor" + }, + "read_only_mode": + "Debido a tareas de mantenimiento de los servidores, solo el modo lectura está disponible. Perdonen las molestias", + "tags_and_topics": "Etiquetas y Temas", + "show_more_topics": "Mostrar más temas", + "basic": "Básico", + "advanced": "Avanzado", + "views": { + "zero": "Ninguna visualización", + "one": "%(count)s Visualización", + "other": "%(count)s Visualizaciones" + }, + "responses": { + "zero": "Sin Respuestas", + "one": "%(count)s Respuesta", + "other": "%(count)s Respuestas" + }, + "post_key_warning": { + "confirm": + "Está a punto de publicar una clave privada STEEM o una contraseña maestra. Probablemente perderá el control de la cuenta asociada y todos sus fondos.", + "warning": + "Los usuarios legítimos, incluidos los empleados de Steemit Inc., nunca le pedirán una clave privada o contraseña maestra.", + "checkbox": "Entiendo" + } + }, + "navigation": { + "about": "Sobre", + "explore": "Explorar", + "APP_NAME_whitepaper": "%(APP_NAME)s Documentación técnica", + "buy_LIQUID_TOKEN": "Comprar %(LIQUID_TOKEN)s", + "sell_LIQUID_TOKEN": "Vender %(LIQUID_TOKEN)s", + "currency_market": "Mercado de monedas", + "stolen_account_recovery": "Recuperación de cuentas robadas", + "change_account_password": "Cambia la contraseña de la cuenta", + "witnesses": "Testigos", + "vote_for_witnesses": "Vota a los testigos", + "privacy_policy": "Política de Privacidad", + "terms_of_service": "Términos del Servicio", + "sign_up": "Unirse", + "learn_more": "Para saber más", + "welcome": "Bienvenido", + "faq": "Preguntas Frecuentes", + "shop": "The Steemit Shop", + "chat": "Chat de Steemit", + "app_center": "Centro de Aplicaciones Steemit", + "api_docs": "Documentos API de Steemit", + "bluepaper": "Steem Bluepaper", + "smt_whitepaper": "Libro blanco de SMT", + "whitepaper": "Libro blanco de Steem", + "intro_tagline": "El dinero habla.", + "intro_paragraph": + "Tu voz tiene valor. Únete a la comunidad que te paga por publicar y votar contenido de alta calidad" + }, + "main_menu": { + "hot": "en alza", + "trending": "tendencia" + }, + "reply_editor": { + "shorten_title": "Abreviar el título", + "exceeds_maximum_length": "Excede la capacidad máxima (%(maxKb)sKB)", + "including_the_category": "(incluyendo categoría '%(rootCategory)s')", + "use_limited_amount_of_tags": + "Tienes %(tagsLength)s tags totales %(includingCategory)s. Por favor usa solo 5 tags en tu post y en la línea de categorías", + "are_you_sure_you_want_to_clear_this_form": + "¿Estás seguro de que quieres limpiar este formulario?", + "uploading": "Subiendo", + "draft_saved": "Borrador guardado.", + "editor": "Editor", + "insert_images_by_dragging_dropping": + "Inserta imágenes arrastrando y soltando,", + "pasting_from_the_clipboard": "pegando desde el portapapeles,", + "selecting_them": "Seleccionándolos", + "image_upload": "Subir imagen", + "power_up_100": "Power Up 100%%", + "default_50_50": "Por defecto (50 1%% / 50 1%%)", + "decline_payout": "Rechazar Pago", + "check_this_to_auto_upvote_your_post": + "Seleciona para autovotarte el post", + "markdown_styling_guide": "Guía para el Markdown", + "or_by": "o por", + "title": "Título", + "update_post": "Actualizar Publicación", + "markdown_not_supported": "Markdown no soportado aquí" + }, + "category_selector_jsx": { + "tag_your_story": + "Etiqueta (hasta 5 etiquetas), la primera etiqueta es tu principal categoría.", + "select_a_tag": "Selecciona una etiqueta", + "maximum_tag_length_is_24_characters": + "La longitud máxima de la etiqueta es de 24 caracteres", + "use_limited_amount_of_categories": + "Por favor usa sólo %(amount)s categorías", + "use_only_lowercase_letters": "Usa sólo letras minúsculas", + "use_one_dash": "Usa sólo un guión", + "use_spaces_to_separate_tags": + "Usa espacios para separar las etiquetas", + "use_only_allowed_characters": + "Usa sólo letras minúsculas, dígitos y un guión", + "must_start_with_a_letter": "Debe empezar con una letra", + "must_end_with_a_letter_or_number": + "Debe terminar con una letra o número" + }, + "postfull_jsx": { + "this_post_is_not_available_due_to_a_copyright_claim": + "Este post no está disponible por reclamación de derechos de autor.", + "share_on_facebook": "Compartir en Facebook", + "share_on_twitter": "Compartir en Twitter", + "share_on_linkedin": "Compartir en Linkedin", + "recent_password": "Contraseña reciente", + "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": + "En 3.5 días, convertir 1 %(amount)s 2 %(DEBT_TOKEN)s a 3 %(LIQUID_TOKEN)s", + "view_the_full_context": "Ver todo el contexto", + "view_the_direct_parent": "Ver el comentario relacionado", + "you_are_viewing_a_single_comments_thread_from": + "Estás viendo los comentarios relacionados de" + }, + "market_jsx": { + "action": "Acción", + "date_created": "Fecha de creación", + "last_price": "Último precio", + "24h_volume": "Volumen de 24 horas", + "spread": "Difundir", + "total": "Total", + "available": "Disponible", + "lowest_ask": "Oferta más baja", + "highest_bid": "Oferta más alta", + "buy_orders": "Comprar órdenes", + "sell_orders": "Vender órdenes", + "trade_history": "Historia de compra venta", + "open_orders": "Órdenes abiertas", + "sell_amount_for_atleast": + "Vender %(amount_to_sell)s al menos por %(min_to_receive)s (%(effectivePrice)s)", + "buy_atleast_amount_for": + "Comprar al menos %(min_to_receive)s for %(amount_to_sell)s (%(effectivePrice)s)", + "price_warning_above": + "Este precio está por debajo del precio de mercado que es %(marketPrice)s, estás seguro?", + "price_warning_below": + "Este precio está por debajo del precio de mercado que es %(marketPrice)s, estás seguro?", + "order_cancel_confirm": "Cancelar órden %(order_id)s de %(user)s?", + "order_cancelled": "Órden %(order_id)s cancelada", + "higher": "Más alta", + "lower": "Más baja", + "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": + "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" + }, + "recoveraccountstep1_jsx": { + "begin_recovery": "Empezar Recuperación", + "not_valid": "No es válido", + "account_name_is_not_found": "Nombre de usuario no encontrado", + "unable_to_recover_account_not_change_ownership_recently": + "No podemos recuperar esta cuenta, esta no ha cambiado su propiedad recientemente.", + "password_not_used_in_last_days": + "Esta contraseña no ha sido usada en esta cuenta en los últimos 30 días.", + "request_already_submitted_contact_support": + "Tu petición ha sido enviada y estamos trabajando en ella. Por favor contacta %(SUPPORT_EMAIL)s para ver el estado de la misma.", + "recover_account_intro": + "Puede ocurrir que la owner key de algún usuario se vea comprometida. La funcionalidad de recuperación de contraseñas robadas le da al propietario de la cuenta 30 días para recuperar su cuenta desde el mismo momento que el delincuente ha cambiado la owner key. Esta funcionalidad sólo puede ser usada en %(APP_URL)s si el propietario de la cuenta ha incluído %(APP_NAME)s como su cuenta de confianza y además a comulgado con las condiciones y términos de servicio de %(APP_NAME)s", + "login_with_facebook_or_reddit_media_to_verify_identity": + "Por favor accede con Fabebook o Reddit para verificar tu identidad", + "login_with_social_media_to_verify_identity": + "Por favor entra con %(provider)s para verificar tu identidad", + "enter_email_toverify_identity": + "Necesitamos verificar tu identidad. Por favor introduce tu dirección de correo electrónico debajo para empezar la verificación.", + "continue_with_email": "Continuar con correo electrónico", + "thanks_for_submitting_request_for_account_recovery": + "Gracias por enviar tu petición a nuestro sistema de recuperación de contraseñas usando %(APP_NAME)s basado en la autentificación multi factor. Tan pronto como sea posible recibirás una respuesta, sim embargo, puede que haya algún retraso debido al alto volumen de peticiones. Por favor, esté preparado para verificar su identidad.", + "recovering_account": "Recuperando cuenta", + "recover_account": "Recuperar Cuenta", + "checking_account_owner": "Comprobando propietario de la cuenta", + "sending_recovery_request": "Enviando solicitud de recuperación ", + "cant_confirm_account_ownership": + "No podemos confirmar la propiedad de la cuenta. Comprueba tu contraseña", + "account_recovery_request_not_confirmed": + "La solicitud de recuperación de cuenta no está confirmada todavía, por favor vuelve más tarde, gracias por tu paciencia." + }, + "user_profile": { + "unknown_account": "Cuenta desconocida", + "user_hasnt_made_any_posts_yet": + "Parece que %(name)s aún no ha comenzado a postear", + "user_hasnt_started_bloggin_yet": + "Parece que %(name)s aún no ha comenzado su blog", + "user_hasnt_followed_anything_yet": + "Parece que %(name)s aún no está siguiendo a nadie. Si %(name)s acaba de añadir nuevas cuentas para seguir, el feed personalizado aparecerá cuando el nuevo contenido esté disponible.", + "user_hasnt_had_any_replies_yet": + "%(name)s todavía no ha tenido ninguna respuesta", + "looks_like_you_havent_posted_anything_yet": + "Looks like you haven't posted anything yet.", + "create_a_post": "Create a Post", + "explore_trending_articles": "Explore Trending Articles", + "read_the_quick_start_guide": "Read The Quick Start Guide", + "browse_the_faq": "Browse The FAQ", + "followers": "Seguidores", + "this_is_users_reputations_score_it_is_based_on_history_of_votes": + "Esto es la reputación de %(name)s's. La reputación está basada en la historia de votos recibidos por la cuenta y se usa para esconder contenido de baja calidad.", + "follower_count": { + "zero": "Sin seguidores", + "one": "1 seguidor", + "other": "%(count)s seguidores" + }, + "followed_count": { + "zero": "No sigue a nadie", + "one": "1 siguiendo", + "other": "%(count)s siguiendo" + }, + "post_count": { + "zero": "Sin publicaciones", + "one": "1 publicación", + "other": "%(count)s posts" + } + }, + "authorrewards_jsx": { + "estimated_author_rewards_last_week": + "Recompensas de autor estimadas la semana pasada", + "author_rewards_history": "Historial de recompensas de autor" + }, + "curationrewards_jsx": { + "estimated_curation_rewards_last_week": + "Recompensas de curación aproximadas de la semana pasada", + "curation_rewards_history": "Historial de recompensas de curación" + }, + "post_jsx": { + "now_showing_comments_with_low_ratings": + "Mostrando ahora comentarios con bajas calificaciones", + "sort_order": "Clase de órden", + "comments_were_hidden_due_to_low_ratings": + "Los comentarios fueron ocultados debido a las bajas calificaciones" + }, + "voting_jsx": { + "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": + "Flagear un posts puede hacer desaparecer las recompensas y hacer el contenido menos visible. Usar el sentido común para flagear", + "disagreement_on_rewards": "Desacuerdo en las recompensas", + "fraud_or_plagiarism": "Fraude o Plagio", + "hate_speech_or_internet_trolling": + "Discurso de odio o trolear en internat", + "intentional_miss_categorized_content_or_spam": + "Uso erróneo de los tags o Spam", + "pending_payout": "Pago pendiente $ %(value)s", + "payout_declined": "Pago rehusado", + "max_accepted_payout": "Máximo pago aceptado $ %(value)s", + "promotion_cost": "Coste de promoción $ %(value)s", + "past_payouts": "Pagos pasados $ %(value)s", + "past_payouts_author": "Autor $ %(value)s", + "past_payouts_curators": "Curador $ %(value)s", + "we_will_reset_curation_rewards_for_this_post": + "Reseteará tus recompensas de curación para este post", + "removing_your_vote": "Quitando tu voto", + "changing_to_an_upvote": "Cambiando a Up-vote", + "changing_to_a_downvote": "Cambiando a Down-Vote", + "confirm_flag": "Confirmar Flag", + "and_more": "y %(count)s más", + "votes_plural": { + "one": "%(count)s voto", + "other": "%(count)s votos" + } + }, + "witnesses_jsx": { + "witness_thread": "tema testigo", + "top_witnesses": "Votación para testigo", + "you_have_votes_remaining": { + "zero": "No tienes votos restantes", + "one": "Tienes 1 voto restante", + "other": "Te quedan %(count)s votos disponibles" + }, + "you_can_vote_for_maximum_of_witnesses": + "Puedes votar como máximo a 30 testigos", + "witness": "Testigos", + "information": "Información", + "if_you_want_to_vote_outside_of_top_enter_account_name": + "SI quieres votar a un testigo que está fuera de los top 50, por favor introduce el nombre de la cuenta para votarle", + "set_witness_proxy": + "También puedes elegir un proxy que votará a los testigos por ti. Esta actividad reseteará tu actual selección de witness", + "witness_set": + "Has seleccionado un proxy de votación. Si quieres volver a habilitar el voto manual, por favor borra el proxy.", + "witness_proxy_current": "Tu proxy actual es", + "witness_proxy_set": "Crear proxy", + "witness_proxy_clear": "Borrar proxy", + "proxy_update_error": "Tu proxy no fue actualizado" + }, + "votesandcomments_jsx": { + "no_responses_yet_click_to_respond": + "Todavía sin respuestas. Pincha para responder.", + "response_count_tooltip": { + "zero": "sin respuestas. Pincha para responder.", + "one": "1 respuesta. Pincha para responder.", + "other": "%(count)s respuestas. Pincha para responder." + }, + "vote_count": { + "zero": "sin votos", + "one": "1 voto", + "other": "%(count)s votos" + } + }, + "userkeys_jsx": { + "public": "Público", + "private": "Privado", + "public_something_key": "Contraseña %(count)s pública", + "private_something_key": "Contraseña %(count)s privada", + "posting_key_is_required_it_should_be_different": + "La contraseña de posteado se usa para postear y para votar. Debería ser diferente a la contraseña activa y a la contraseña de propietario.", + "the_active_key_is_used_to_make_transfers_and_place_orders": + "La contraseña activa se usa para hacer transferencias y poner órdenes de compra o venta en el mercado interno.", + "the_owner_key_is_required_to_change_other_keys": + "La contraseña propia es la contraseña maestra para la cuenta y se requiere para cambiar el resto de las contraseñas.", + "the_private_key_or_password_should_be_kept_offline": + "La contraseña privada para la cuenta propia debería estar fuera del acceso a internet. Se recomienda imprimirla y guardarla en lugar seguro.", + "the_memo_key_is_used_to_create_and_read_memos": + "La contraseña memo se usa para crear y leer memos." + }, + "suggestpassword_jsx": { + "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": + "%(APP_NAME)s no puede recuperar contraseñas. Guarda está página en lugar seguro, como en una caja fuerte o una caja de depósito", + "APP_NAME_password_backup": + "%(APP_NAME)s Contraseña de apoyo o seguridad", + "APP_NAME_password_backup_required": + "%(APP_NAME)s Contraseña de apoyo o seguridad (requerida)", + "after_printing_write_down_your_user_name": + "Después de imprimir, escribe tu nombre de usuario" + }, + "converttosteem_jsx": { + "your_existing_DEBT_TOKEN_are_liquid_and_transferable": + "La cantidad de %(DEBT_TOKEN)s son líquidos y transferibles. Si lo deseas también puedes comprar o vender %(DEBT_TOKEN)s directamente en esta página en el %(link)s o transferir a un mercado externo", + "this_is_a_price_feed_conversion": + "Esto es la conversión de precio. Los 3.5 días de retraso son necesarios para prevenir el abuso con las medias del precio", + "convert_to_LIQUID_TOKEN": "Convertir a %(LIQUID_TOKEN)s", + "DEBT_TOKEN_will_be_unavailable": + "Esta acción tardará 3.5 días en procesarse y no puede ser cancelada. Estos %(DEBT_TOKEN)s dejarán inmediatamente de estar disponibles" + }, + "tips_js": { + "liquid_token": + "Tokens canjeables pueden ser transferidos a cualquier sitio en cualquier momento.1%(LIQUID_TOKEN)s y pueden ser convertidos a %(VESTING_TOKEN)s en un proceso llamado power up.", + "influence_token": + "Tokens con influencia te dan más control sobre los pagos de los posts y te permiten ganar recompensas de curación", + "estimated_value": + "El valor estimado está basado en el valor medio de 1%(LIQUID_TOKEN)s en US dollars.", + "non_transferable": + "%(VESTING_TOKEN)s es intransferible y requiere 3 meses (13 pagos) para convertir de vuelta a %(LIQUID_TOKEN)s.", + "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": + "Los %(VESTING_TOKEN)s convertidos pueden ser enviados a ti mismo o a otras personas pero no pueden ser transferidos de nuevo sin convertirlos de vuelta a %(LIQUID_TOKEN)s.", + "part_of_your_steem_power_is_currently_delegated": + "Parte de tu STEEM POWER te ha sido delegado. La delegación es influencia donada que ayuda a los nuevos usuarios a interactuar en steemit. La cantidad de tu delegación puede fluctuar." + }, + "promote_post_jsx": { + "promote_post": "Promociona el Post", + "spend_your_DEBT_TOKEN_to_advertise_this_post": + "Gasta tu %(DEBT_TOKEN)s para promocionar tu contenido en la sección de promoción", + "you_successfully_promoted_this_post": + "Has promocionado satisfactoriamente este post", + "this_post_was_hidden_due_to_low_ratings": + "El post ha sido ocultado por bajas calificaciones" + }, + "about_jsx": { + "about_app": "Sobre %(APP_NAME)s", + "about_app_details": + "%(APP_NAME)s es una plataforma social online donde todo el mundo genera ingresos creando y votando contenido. Usa un sistema muy sólido de puntos digitales llamado Steem que soporta valor real gracias al precio de mercado y gracias a su liquidez.", + "learn_more_at_app_url": "Aprende más en %(APP_URL)s", + "resources": "Recursos" + }, + "markdownviewer_jsx": { + "images_were_hidden_due_to_low_ratings": + "Las imágenes fueron ocultadas por bajas calificaciones." + }, + "postsummary_jsx": { + "resteemed": "reesteemeado", + "resteemed_by": "Resteemeado por", + "reveal_it": "revelarlo", + "adjust_your": "ajustar tu", + "display_preferences": "Visualizar preferencias", + "create_an_account": "crea una cuenta", + "to_save_your_preferences": "para guardar tus preferencias" + }, + "posts_index": { + "empty_feed_1": "Parece que no has seguido nada todavía", + "empty_feed_2": + "Si recientemente has añadido a gente para seguir, tu feed personalizado aparecerá una vez que el contenido esté disponible", + "empty_feed_3": "Explorar artículos en tendencia", + "empty_feed_4": "leer la guía básica para comenzar", + "empty_feed_5": "Explorar la FAQ" + }, + "transferhistoryrow_jsx": { + "to_savings": "a ahorros", + "from_savings": "desde ahorros", + "cancel_transfer_from_savings": "Cancela transferencia desde ahorros", + "stop_power_down": "Para power down", + "start_power_down_of": "Comenzar power down", + "receive_interest_of": "Recibir interés de" + }, + "savingswithdrawhistory_jsx": { + "cancel_this_withdraw_request": "¿Cancelar la petición de retirada?", + "pending_savings_withdrawals": "AHORROS PENDIENTES DE RETIRADA", + "withdraw": "Retirar %(amount)s", + "to": "a %(to)s", + "from_to": "de %(from)s a %(to)s" + }, + "explorepost_jsx": { + "copied": "¡Copiado!", + "copy": "COPIAR", + "alternative_sources": "Fuentes alternativas" + }, + "header_jsx": { + "home": "inicio", + "create_a_post": "Crear una publicación", + "change_account_password": "Cambiar Contraseña de la Cuenta", + "create_account": "Crear cuenta", + "stolen_account_recovery": "Recuperación de cuentas robadas", + "people_following": "Gente que sigues", + "people_followed_by": "Gente seguida por", + "curation_rewards_by": "Recompensas de curación de", + "author_rewards_by": "Recompensas de autor de", + "replies_to": "Respuestas a", + "comments_by": "Comentarios de" + }, + "loginform_jsx": { + "you_need_a_private_password_or_key": + "Necesitas la contraseña privada (no la contraseña pública)", + "cryptography_test_failed": "El test cryptográfico no tuvo éxito", + "unable_to_log_you_in": "No puedes acceder con este navegador", + "the_latest_versions_of": "Las últimas versiones de", + "are_well_tested_and_known_to_work_with": + "han sido testeados con éxito y están preparados para trabajar con %(APP_URL)s.", + "due_to_server_maintenance": + "Debido a tareas de mantenimiento sólo estamos disponibles en modo lectura. Pedimos disculpas por los inconvenientes", + "login_to_vote": "Acceso para Votar", + "login_to_post": "Acceso para postear", + "login_to_comment": "Acceso para comentar", + "posting": "Postear", + "active_or_owner": "Activa o propietaria", + "this_password_is_bound_to_your_account_owner_key": + "Esta contraseña está ligada a tu contraseña owner o propia y no puede ser usada para acceder a esta página.", + "however_you_can_use_it_to": "Sin embargo, puedes usarla para", + "update_your_password": "cambiar tu contraseña", + "to_obtain_a_more_secure_set_of_keys": + "obtener un grupo más seguro de contraseñas.", + "this_password_is_bound_to_your_account_active_key": + "Esta contraseña está ligada tu contraseña activa y no puede ser usada para acceder a esta página.", + "you_may_use_this_active_key_on_other_more": + "Puede usar la contraseña activa en otras páginas más seguras como el Monedero.", + "you_account_has_been_successfully_created": + "Tu cuenta se ha creado con éxito!", + "you_account_has_been_successfully_recovered": + "Tu cuenta ha sido recuperada con éxito!", + "password_update_succes": + "La contraseña %(accountName)s ha sido cambiada correctamente", + "password_info": + "La contraseña o la contraseña privada fue introducida incorrectamente. Probablemente. Ayuda: Las contraseñas generadas por Steemit nunca contendrán 0 (cero), O (o mayúscula), I (i mayúscula), l (L minúscula).", + "enter_your_username": "Introduce tu nombre de usuario", + "password_or_wif": "Contraseña o WIF", + "this_operation_requires_your_key_or_master_password": + "Esta operación requiere %(authType)s contraseña o la contraseña maestra", + "keep_me_logged_in": "Manténme logeado", + "amazing_community": "comunidad increíble", + "to_comment_and_reward_others": "para comentar y recompensar a otros", + "sign_up_get_steem": "Crea una cuenta. Comienza en STEEM", + "signup_button": "Crea una cuenta, comienza a ganar ", + "signup_button_emphasis": "FREE STEEM!", + "returning_users": "Inicia sesion", + "join_our": "Únete a nuestro" + }, + "chainvalidation_js": { + "account_name_should": "El nombre de la cuenta debería", + "not_be_empty": "No estar vacío", + "be_longer": "ser más largo", + "be_shorter": "ser más corto.", + "each_account_segment_should": "Cada segmento de la cuenta debería", + "start_with_a_letter": "comenzar con una letra.", + "have_only_letters_digits_or_dashes": + "tener sólo letras, dígitos o guiones.", + "have_only_one_dash_in_a_row": "tener sólo un guión consecutivo.", + "end_with_a_letter_or_digit": "terminar con una legra o dígito.", + "verified_exchange_no_memo": + "Tienes que incluir un memo para la transferencia al exchange" + }, + "settings_jsx": { + "invalid_url": "URL inválida", + "name_is_too_long": "El nombre es demasiado largo.", + "name_must_not_begin_with": "El nombre no debe empezar con @", + "about_is_too_long": "Sobre es demasiado largo", + "location_is_too_long": "Localización es demasiado largo", + "website_url_is_too_long": "La URL de la página web es muy larga", + "public_profile_settings": "Herramientas para el perfil público", + "private_post_display_settings": + "Herramientas privadas para la visualización del post", + "not_safe_for_work_nsfw_content": "(NSFW) contenido para adultos", + "always_hide": "Ocultar siempre", + "always_warn": "Advertir siempre", + "always_show": "Mostrar siempre", + "muted_users": "Usuarios silenciados", + "update": "Actualizar", + "profile_image_url": "Link para la foto de perfil", + "cover_image_url": "Link para la imagen de fondo", + "profile_name": "Nombre de visualización", + "profile_about": "Sobre", + "profile_location": "Localización", + "profile_website": "Página Web" + }, + "transfer_jsx": { + "amount_is_in_form": "La cantidad está en el formato 99999.999", + "insufficient_funds": "Saldo insuficiente", + "use_only_3_digits_of_precison": "Usa solo 3 dígitos", + "send_to_account": "Mandar a cuenta", + "asset": "Activo", + "this_memo_is_private": "Esta memo es privada", + "this_memo_is_public": "Esta memo es pública", + "convert_to_VESTING_TOKEN": "Convertir a %(VESTING_TOKEN)s", + "balance_subject_to_3_day_withdraw_waiting_period": + "El saldo está sujeto a 3 días de espera para la retirada", + "move_funds_to_another_account": + "Mover fondos a otra %(APP_NAME)scuenta.", + "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": + "Protege los fondos al pedir la retirada con un periodo de 3 días de espera", + "withdraw_funds_after_the_required_3_day_waiting_period": + "Retirada de fondos después del periodo de 3 días de espera", + "from": "De", + "to": "Para", + "asset_currently_collecting": + "%(asset)s recolectando actualmente %(interest)s%% APR.", + "beware_of_spam_and_phishing_links": + "Tenga cuidado con los enlaces de spam y phishing en los memos de transferencia. No abra enlaces de usuarios en los que no confíe. No proporcione sus claves privadas a sitios web de terceros." + }, + "userwallet_jsx": { + "conversion_complete_tip": "Se completará el", + "in_conversion": "%(amount)s en conversión", + "transfer_to_savings": "Transferir a ahorros", + "power_up": "Power up", + "power_down": "Power down", + "market": "Mercado", + "convert_to_LIQUID_TOKEN": "Convertir a %(LIQUID_TOKEN)s", + "withdraw_LIQUID_TOKEN": "Retirar %(LIQUID_TOKEN)s", + "withdraw_DEBT_TOKENS": "Retirar %(DEBT_TOKENS)s", + "tokens_worth_about_1_of_LIQUID_TICKER": + "Los tokens valen alrededor de $1.00 de %(LIQUID_TICKER)s, ahora mismo recolectando %(sbdInterest)s%% APR.", + "savings": "AHORROS", + "estimated_account_value": "Valor de cuenta aproximado", + "next_power_down_is_scheduled_to_happen": + "El siguiente power down ocurrirá en ", + "transfers_are_temporary_disabled": + "Las transferencias están temporalmente inhabilitadas.", + "history": "HISTORIA", + "redeem_rewards": "Liquidar recompensas (Transferir al saldo)", + "buy_steem_or_steem_power": "Compra STEEM o STEEM POWER." + }, + "powerdown_jsx": { + "power_down": "Power Down", + "amount": "Cantidad", + "already_power_down": + "Estás haciendo un power down de %(AMOUNT)s%(LIQUID_TICKER)s (%(WITHDRAWN)s%(LIQUID_TICKER)shan sido pagados hasta ahora). Ten en cuenta que si cambias la cantidad de \"power down\" la fecha de pago se reinicializará.", + "delegating": + "Estás delegando %(AMOUNT)s%(LIQUID_TICKER)s. Esa cantidad está \"bloquedada\" y no podrás hacer \"power down\" con ella hasta que hayas cancelado la delegación y el periodo de pagos haya vencido.", + "per_week": "Eso es %(AMOUNT)s%(LIQUID_TICKER)spor semana", + "warning": + "Dejar menos de %(AMOUNT)s%(VESTING_TOKEN)s en su cuenta no es recomendable y puede dejarla en un estado inutilizable.", + "error": "Imposible hacer \"power down\" (ERROR: %(MESSAGE)s)" + }, + "checkloginowner_jsx": { + "your_password_permissions_were_reduced": + "Sus permisos de identificacion han sido limitados.", + "if_you_did_not_make_this_change": "Si no ha hecho el cambio por favor", + "ownership_changed_on": "Pertenencia cambio el", + "deadline_for_recovery_is": "La fecha límite para la recuperación es", + "i_understand_dont_show_again": "Lo entiendo, no mostrarmelo de nuevo." } - }, - "userkeys_jsx": { - "public": "Público", - "private": "Privado", - "public_something_key": "Contraseña %(count)s pública", - "private_something_key": "Contraseña %(count)s privada", - "posting_key_is_required_it_should_be_different": "La contraseña de posteado se usa para postear y para votar. Debería ser diferente a la contraseña activa y a la contraseña de propietario.", - "the_active_key_is_used_to_make_transfers_and_place_orders": "La contraseña activa se usa para hacer transferencias y poner órdenes de compra o venta en el mercado interno.", - "the_owner_key_is_required_to_change_other_keys": "La contraseña propia es la contraseña maestra para la cuenta y se requiere para cambiar el resto de las contraseñas.", - "the_private_key_or_password_should_be_kept_offline": "La contraseña privada para la cuenta propia debería estar fuera del acceso a internet. Se recomienda imprimirla y guardarla en lugar seguro.", - "the_memo_key_is_used_to_create_and_read_memos": "La contraseña memo se usa para crear y leer memos." - }, - "suggestpassword_jsx": { - "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": "%(APP_NAME)s no puede recuperar contraseñas. Guarda está página en lugar seguro, como en una caja fuerte o una caja de depósito", - "APP_NAME_password_backup": "%(APP_NAME)s Contraseña de apoyo o seguridad", - "APP_NAME_password_backup_required": "%(APP_NAME)s Contraseña de apoyo o seguridad (requerida)", - "after_printing_write_down_your_user_name": "Después de imprimir, escribe tu nombre de usuario" - }, - "converttosteem_jsx": { - "your_existing_DEBT_TOKEN_are_liquid_and_transferable": "La cantidad de %(DEBT_TOKEN)s son líquidos y transferibles. Si lo deseas también puedes comprar o vender %(DEBT_TOKEN)s directamente en esta página en el %(link)s o transferir a un mercado externo", - "this_is_a_price_feed_conversion": "Esto es la conversión de precio. Los 3.5 días de retraso son necesarios para prevenir el abuso con las medias del precio", - "convert_to_LIQUID_TOKEN": "Convertir a %(LIQUID_TOKEN)s", - "DEBT_TOKEN_will_be_unavailable": "Esta acción tardará 3.5 días en procesarse y no puede ser cancelada. Estos %(DEBT_TOKEN)s dejarán inmediatamente de estar disponibles" - }, - "tips_js": { - "liquid_token": "Tokens canjeables pueden ser transferidos a cualquier sitio en cualquier momento.1%(LIQUID_TOKEN)s y pueden ser convertidos a %(VESTING_TOKEN)s en un proceso llamado power up.", - "influence_token": "Tokens con influencia te dan más control sobre los pagos de los posts y te permiten ganar recompensas de curación", - "estimated_value": "El valor estimado está basado en el valor medio de 1%(LIQUID_TOKEN)s en US dollars.", - "non_transferable": "%(VESTING_TOKEN)s es intransferible y requiere 3 meses (13 pagos) para convertir de vuelta a %(LIQUID_TOKEN)s.", - "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": "Los %(VESTING_TOKEN)s convertidos pueden ser enviados a ti mismo o a otras personas pero no pueden ser transferidos de nuevo sin convertirlos de vuelta a %(LIQUID_TOKEN)s.", - "part_of_your_steem_power_is_currently_delegated": "Parte de tu STEEM POWER te ha sido delegado. La delegación es influencia donada que ayuda a los nuevos usuarios a interactuar en steemit. La cantidad de tu delegación puede fluctuar." - }, - "promote_post_jsx": { - "promote_post": "Promociona el Post", - "spend_your_DEBT_TOKEN_to_advertise_this_post": "Gasta tu %(DEBT_TOKEN)s para promocionar tu contenido en la sección de promoción", - "you_successfully_promoted_this_post": "Has promocionado satisfactoriamente este post", - "this_post_was_hidden_due_to_low_ratings": "El post ha sido ocultado por bajas calificaciones" - }, - "about_jsx": { - "about_app": "Sobre %(APP_NAME)s", - "about_app_details": "%(APP_NAME)s es una plataforma social online donde todo el mundo genera ingresos creando y votando contenido. Usa un sistema muy sólido de puntos digitales llamado Steem que soporta valor real gracias al precio de mercado y gracias a su liquidez.", - "learn_more_at_app_url": "Aprende más en %(APP_URL)s", - "resources": "Recursos" - }, - "markdownviewer_jsx": { - "images_were_hidden_due_to_low_ratings": "Las imágenes fueron ocultadas por bajas calificaciones." - }, - "postsummary_jsx": { - "resteemed": "reesteemeado", - "resteemed_by": "Resteemeado por", - "reveal_it": "revelarlo", - "adjust_your": "ajustar tu", - "display_preferences": "Visualizar preferencias", - "create_an_account": "crea una cuenta", - "to_save_your_preferences": "para guardar tus preferencias" - }, - "posts_index": { - "empty_feed_1": "Parece que no has seguido nada todavía", - "empty_feed_2": "Si recientemente has añadido a gente para seguir, tu feed personalizado aparecerá una vez que el contenido esté disponible", - "empty_feed_3": "Explorar artículos en tendencia", - "empty_feed_4": "leer la guía básica para comenzar", - "empty_feed_5": "Explorar la FAQ" - }, - "transferhistoryrow_jsx": { - "to_savings": "a ahorros", - "from_savings": "desde ahorros", - "cancel_transfer_from_savings": "Cancela transferencia desde ahorros", - "stop_power_down": "Para power down", - "start_power_down_of": "Comenzar power down", - "receive_interest_of": "Recibir interés de" - }, - "savingswithdrawhistory_jsx": { - "cancel_this_withdraw_request": "¿Cancelar la petición de retirada?", - "pending_savings_withdrawals": "AHORROS PENDIENTES DE RETIRADA", - "withdraw": "Retirar %(amount)s", - "to": "a %(to)s", - "from_to": "de %(from)s a %(to)s" - }, - "explorepost_jsx": { - "copied": "¡Copiado!", - "copy": "COPIAR", - "alternative_sources": "Fuentes alternativas" - }, - "header_jsx": { - "home": "inicio", - "create_a_post": "Crear una publicación", - "change_account_password": "Cambiar Contraseña de la Cuenta", - "create_account": "Crear cuenta", - "stolen_account_recovery": "Recuperación de cuentas robadas", - "people_following": "Gente que sigues", - "people_followed_by": "Gente seguida por", - "curation_rewards_by": "Recompensas de curación de", - "author_rewards_by": "Recompensas de autor de", - "replies_to": "Respuestas a", - "comments_by": "Comentarios de" - }, - "loginform_jsx": { - "you_need_a_private_password_or_key": "Necesitas la contraseña privada (no la contraseña pública)", - "cryptography_test_failed": "El test cryptográfico no tuvo éxito", - "unable_to_log_you_in": "No puedes acceder con este navegador", - "the_latest_versions_of": "Las últimas versiones de", - "are_well_tested_and_known_to_work_with": "han sido testeados con éxito y están preparados para trabajar con %(APP_URL)s.", - "due_to_server_maintenance": "Debido a tareas de mantenimiento sólo estamos disponibles en modo lectura. Pedimos disculpas por los inconvenientes", - "login_to_vote": "Acceso para Votar", - "login_to_post": "Acceso para postear", - "login_to_comment": "Acceso para comentar", - "posting": "Postear", - "active_or_owner": "Activa o propietaria", - "this_password_is_bound_to_your_account_owner_key": "Esta contraseña está ligada a tu contraseña owner o propia y no puede ser usada para acceder a esta página.", - "however_you_can_use_it_to": "Sin embargo, puedes usarla para", - "update_your_password": "cambiar tu contraseña", - "to_obtain_a_more_secure_set_of_keys": "obtener un grupo más seguro de contraseñas.", - "this_password_is_bound_to_your_account_active_key": "Esta contraseña está ligada tu contraseña activa y no puede ser usada para acceder a esta página.", - "you_may_use_this_active_key_on_other_more": "Puede usar la contraseña activa en otras páginas más seguras como el Monedero.", - "you_account_has_been_successfully_created": "Tu cuenta se ha creado con éxito!", - "you_account_has_been_successfully_recovered": "Tu cuenta ha sido recuperada con éxito!", - "password_update_succes": "La contraseña %(accountName)s ha sido cambiada correctamente", - "password_info": "La contraseña o la contraseña privada fue introducida incorrectamente. Probablemente. Ayuda: Las contraseñas generadas por Steemit nunca contendrán 0 (cero), O (o mayúscula), I (i mayúscula), l (L minúscula).", - "enter_your_username": "Introduce tu nombre de usuario", - "password_or_wif": "Contraseña o WIF", - "this_operation_requires_your_key_or_master_password": "Esta operación requiere %(authType)s contraseña o la contraseña maestra", - "keep_me_logged_in": "Manténme logeado", - "amazing_community": "comunidad increíble", - "to_comment_and_reward_others": "para comentar y recompensar a otros", - "sign_up_get_steem": "Crea una cuenta. Comienza en STEEM", - "signup_button": "Crea una cuenta, comienza a ganar ", - "signup_button_emphasis": "FREE STEEM!", - "returning_users": "Inicia sesion", - "join_our": "Únete a nuestro" - }, - "chainvalidation_js": { - "account_name_should": "El nombre de la cuenta debería", - "not_be_empty": "No estar vacío", - "be_longer": "ser más largo", - "be_shorter": "ser más corto.", - "each_account_segment_should": "Cada segmento de la cuenta debería", - "start_with_a_letter": "comenzar con una letra.", - "have_only_letters_digits_or_dashes": "tener sólo letras, dígitos o guiones.", - "have_only_one_dash_in_a_row": "tener sólo un guión consecutivo.", - "end_with_a_letter_or_digit": "terminar con una legra o dígito.", - "verified_exchange_no_memo": "Tienes que incluir un memo para la transferencia al exchange" - }, - "settings_jsx": { - "invalid_url": "URL inválida", - "name_is_too_long": "El nombre es demasiado largo.", - "name_must_not_begin_with": "El nombre no debe empezar con @", - "about_is_too_long": "Sobre es demasiado largo", - "location_is_too_long": "Localización es demasiado largo", - "website_url_is_too_long": "La URL de la página web es muy larga", - "public_profile_settings": "Herramientas para el perfil público", - "private_post_display_settings": "Herramientas privadas para la visualización del post", - "not_safe_for_work_nsfw_content": "(NSFW) contenido para adultos", - "always_hide": "Ocultar siempre", - "always_warn": "Advertir siempre", - "always_show": "Mostrar siempre", - "muted_users": "Usuarios silenciados", - "update": "Actualizar", - "profile_image_url": "Link para la foto de perfil", - "cover_image_url": "Link para la imagen de fondo", - "profile_name": "Nombre de visualización", - "profile_about": "Sobre", - "profile_location": "Localización", - "profile_website": "Página Web" - }, - "transfer_jsx": { - "amount_is_in_form": "La cantidad está en el formato 99999.999", - "insufficient_funds": "Saldo insuficiente", - "use_only_3_digits_of_precison": "Usa solo 3 dígitos", - "send_to_account": "Mandar a cuenta", - "asset": "Activo", - "this_memo_is_private": "Esta memo es privada", - "this_memo_is_public": "Esta memo es pública", - "convert_to_VESTING_TOKEN": "Convertir a %(VESTING_TOKEN)s", - "balance_subject_to_3_day_withdraw_waiting_period": "El saldo está sujeto a 3 días de espera para la retirada", - "move_funds_to_another_account": "Mover fondos a otra %(APP_NAME)scuenta.", - "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": "Protege los fondos al pedir la retirada con un periodo de 3 días de espera", - "withdraw_funds_after_the_required_3_day_waiting_period": "Retirada de fondos después del periodo de 3 días de espera", - "from": "De", - "to": "Para", - "asset_currently_collecting": "%(asset)s recolectando actualmente %(interest)s%% APR.", - "beware_of_spam_and_phishing_links": "Tenga cuidado con los enlaces de spam y phishing en los memos de transferencia. No abra enlaces de usuarios en los que no confíe. No proporcione sus claves privadas a sitios web de terceros." - }, - "userwallet_jsx": { - "conversion_complete_tip": "Se completará el", - "in_conversion": "%(amount)s en conversión", - "transfer_to_savings": "Transferir a ahorros", - "power_up": "Power up", - "power_down": "Power down", - "market": "Mercado", - "convert_to_LIQUID_TOKEN": "Convertir a %(LIQUID_TOKEN)s", - "withdraw_LIQUID_TOKEN": "Retirar %(LIQUID_TOKEN)s", - "withdraw_DEBT_TOKENS": "Retirar %(DEBT_TOKENS)s", - "tokens_worth_about_1_of_LIQUID_TICKER": "Los tokens valen alrededor de $1.00 de %(LIQUID_TICKER)s, ahora mismo recolectando %(sbdInterest)s%% APR.", - "savings": "AHORROS", - "estimated_account_value": "Valor de cuenta aproximado", - "next_power_down_is_scheduled_to_happen": "El siguiente power down ocurrirá en ", - "transfers_are_temporary_disabled": "Las transferencias están temporalmente inhabilitadas.", - "history": "HISTORIA", - "redeem_rewards": "Liquidar recompensas (Transferir al saldo)", - "buy_steem_or_steem_power": "Compra STEEM o STEEM POWER." - }, - "powerdown_jsx": { - "power_down": "Power Down", - "amount": "Cantidad", - "already_power_down": "Estás haciendo un power down de %(AMOUNT)s%(LIQUID_TICKER)s (%(WITHDRAWN)s%(LIQUID_TICKER)shan sido pagados hasta ahora). Ten en cuenta que si cambias la cantidad de \"power down\" la fecha de pago se reinicializará.", - "delegating": "Estás delegando %(AMOUNT)s%(LIQUID_TICKER)s. Esa cantidad está \"bloquedada\" y no podrás hacer \"power down\" con ella hasta que hayas cancelado la delegación y el periodo de pagos haya vencido.", - "per_week": "Eso es %(AMOUNT)s%(LIQUID_TICKER)spor semana", - "warning": "Dejar menos de %(AMOUNT)s%(VESTING_TOKEN)s en su cuenta no es recomendable y puede dejarla en un estado inutilizable.", - "error": "Imposible hacer \"power down\" (ERROR: %(MESSAGE)s)" - }, - "checkloginowner_jsx": { - "your_password_permissions_were_reduced": "Sus permisos de identificacion han sido limitados.", - "if_you_did_not_make_this_change": "Si no ha hecho el cambio por favor", - "ownership_changed_on": "Pertenencia cambio el", - "deadline_for_recovery_is": "La fecha límite para la recuperación es", - "i_understand_dont_show_again": "Lo entiendo, no mostrarmelo de nuevo." - } } diff --git a/src/app/locales/fr.json b/src/app/locales/fr.json index 9df8c6b3d..4bad1ff82 100644 --- a/src/app/locales/fr.json +++ b/src/app/locales/fr.json @@ -1,649 +1,794 @@ { - "g": { - "age": "ancienneté", - "amount": "Montant", - "and": "et", - "are_you_sure": "Etes-vous sûr(e) ?", - "ask": "Demande", - "balance": "Solde", - "balances": "Soldes", - "bid": "Offre", - "blog": "Blog", - "browse": "Naviguer", - "buy": "Acheter", - "buy_or_sell": "Acheter ou vendre", - "by": "par", - "cancel": "Annuler", - "change_password": "Changer le mot de passe", - "choose_language": "Choisir la langue", - "clear": "Effacer", - "close": "Fermer", - "collapse_or_expand": "Compacter / Etendre", - "comments": "Commentaires", - "confirm": "Confirmer", - "convert": "Convertir", - "date": "Date", - "delete": "Supprimer", - "dismiss": "Ignorer", - "edit": "Editer", - "email": "Email", - "feed": "Flux", - "follow": "S'abonner", - "for": "pour", - "from": "de", - "go_back": "Retour", - "hide": "Masquer", - "in": "dans", - "in_reply_to": "en réponse à", - "insufficient_balance": "Solde insuffisant", - "invalid_amount": "Montant invalide", - "joined": "Inscrit en", - "loading": "Chargement en cours", - "login": "Se connecter", - "logout": "Déconnexion", - "memo": "Note", - "mute": "Ignorer", - "new": "nouveau", - "newer": "Plus récent", - "next": "Suivant", - "no": "Non", - "ok": "Ok", - "older": "Plus ancien", - "or": "ou", - "order_placed": "Ordre effectué", - "password": "Mot de passe", - "payouts": "Gains", - "permissions": "Permissions", - "phishy_message": "Link expanded to plain text; beware of a potential phishing attempt", - "post": "Poster", - "post_as": "Poster en tant que", - "posts": "Articles", - "powered_up_100": "Converti en influence à 100%%", - "preview": "Prévisualiser", - "previous": "Précédent", - "price": "Prix", - "print": "Imprimer", - "promote": "Promouvoir", - "promoted": "promu", - "re": "RE", - "re_to": "RE :%(topic)s", - "recent_password": "Mot de passe antérieur", - "receive": "Recevoir", - "remove": "Enlever", - "remove_vote": "Enlever son vote", - "replied_to": "répondu à %(account)s", - "replies": "Réponses", - "reply": "Répondre", - "reply_count": { - "zero": "pas de réponse", - "one": "1 réponse", - "other": "%(count)s réponses" - }, - "reputation": "Réputation", - "reveal_comment": "Révéler le commentaire", - "request": "requête", - "required": "Requis", - "rewards": "Gains", - "save": "Sauvegarder", - "saved": "Sauvegardé", - "search": "Rechercher", - "sell": "Vendre", - "settings": "Configuration", - "share_this_post": "Partager cet article", - "show": "Montrer", - "sign_in": "S'inscrire", - "sign_up": "S'inscrire", - "since": "depuis", - "submit": "Soumettre", - "power_up": "Augmenter son influence", - "submit_a_story": "Poster", - "tag": "Mot clé", - "to": "vers", - "all_tags": "All tags", - "transfer": "Transférer", - "trending_topics": "Sujets en vogue", - "type": "Genre", - "unfollow": "Se désabonner", - "unmute": "Ne plus ignorer", - "unknown": "Inconnu", - "upvote": "Voter pour", - "upvote_post": "Voter pour l'article", - "username": "Identifiant", - "version": "Version", - "vote": "Voter", - "votes": "votes", - "wallet": "Portefeuille", - "warning": "alarme", - "yes": "Oui", - "posting": "de publication", - "owner": "propriétaire", - "active": "active", - "account_not_found": "Compte inconnu", - "this_is_wrong_password": "Mot de passe incorrect", - "do_you_need_to": "Avez-vous besoin de", - "account_name": "Nom du compte", - "recover_your_account": "Récupérer son compte", - "reset_usernames_password": "Réinitialiser le mot de passe de %(username)s", - "this_will_update_usernames_authtype_key": "Ceci mettra a jour la clé %(authType)s de %(username)s", - "passwords_do_not_match": "Mots de passe différents", - "you_need_private_password_or_key_not_a_public_key": "La clé ou le mot de passe privé (et non public) est nécessaire ", - "the_rules_of_APP_NAME": { - "one": "La première règle de %(APP_NAME)s est de ne pas perdre son mot de passe.", - "second": "La deuxième règle de %(APP_NAME)s est de ne pas perdre son mot de passe.", - "third": "La troisième règle de %(APP_NAME)s est que nous ne pouvons réinitialiser votre mot de passe.", - "fourth": "La quatrième règle est que si vous pouvez vous rappeler de votre mot de passe, il n'est pas assez sécuritaire.", - "fifth": "La cinquième règle demande d'utiliser uniquement des mots de passe générés aléatoirement.", - "sixth": "La sixième règle est de ne donner à personne son mot de passe.", - "seventh": "La septième règle est de faire une sauvegarde de son mot de passe." - }, - "recover_password": "Récupérer un compte", - "current_password": "Mot de passe actuel", - "generated_password": "Mot de passe généré", - "backup_password_by_storing_it": "A sauvegarder en le copiant dans un fichier texte ou via un logiciel de gestion de mot de passe", - "enter_account_show_password": "Entrer un identifiant valide pour afficher le mot de passe", - "click_to_generate_password": "Cliquer ici pour générer un mot de passe", - "re_enter_generate_password": "Recopier le mot de passe généré", - "understand_that_APP_NAME_cannot_recover_password": "Je comprends que %(APP_NAME)s ne peut récupérer les mots de passe perdus", - "i_saved_password": "J'ai sauvegardé mon mot de passe de façon sécuritaire", - "update_password": "Mettre à jour le mot de passe", - "confirm_password": "Confirmer le mot de passe", - "account_updated": "Compte mis à jour", - "password_must_be_characters_or_more": "Le mot de passe doit comporter au moins %(amount)s caractères supplémentaires", - "need_password_or_key": "Il vous faut une clé ou un mot de passe privé (et non public)", - "login_to_see_memo": "S'identifier pour voir le mémo", - "new_password": "Nouveau mot de passe", - "incorrect_password": "Mot de passe incorrect", - "username_does_not_exist": "Cet identifiant n'existe pas", - "account_name_should_start_with_a_letter": "L'identifiant doit commencer par une lettre.", - "account_name_should_be_shorter": "L'identifiant doit être plus court.", - "account_name_should_be_longer": "L'identifiant doit être plus long.", - "account_name_should_have_only_letters_digits_or_dashes": "L'identifiant ne peut comporter que des lettres, des chiffres ou des traits d'union.", - "cannot_increase_reward_of_post_within_the_last_minute_before_payout": "Les gains d'un article ne peuvent être accrus durant la dernière minute avant rétribution.", - "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": "un vote existe actuellement; l'utilisateur doit indiquer une raison pour rejeter le témoin", - "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": "Un seul compte Steem est permis par adresse IP toutes les 10 minutes", - "resteem_this_post": "Resteemer cet article", - "reblog": "Resteem", - "write_your_story": "Ecris ton histoire", - "remember_voting_and_posting_key": "Se souvenir de la clé de publication et de vote", - "auto_login_question_mark": "Identification automatique", - "hide_private_key": "Masquer la clé privée", - "show_private_key": "Révéler la clé privée", - "login_to_show": "S'identifier pour révéler", - "not_valid_email": "Adresse email invalide", - "thank_you_for_being_an_early_visitor_to_APP_NAME": "Merci pour être l'un des premiers visiteurs de %(APP_NAME)s. Nous reviendrons vers vous aussi tôt que possible.", - "author_rewards": "Gains éditoriaux", - "curation_rewards": "Gains de curation", - "sorry_your_reddit_account_doesnt_have_enough_karma": "Désolé, votre compte Reddit ne possède pas de Karma Reddit suffisant pour vous qualifier pour une inscription gratuite. Veuillez s'il-vous-plaît indiquer votre adresse email pour être placé(e) sur liste d'attente.", - "register_with_facebook": "S'enregistrer par Facebook", - "or_click_the_button_below_to_register_with_facebook": "Ou cliquer sur le bouton ci-dessous pour vous enregistrer par Facebook", - "server_returned_error": "le serveur a renvoyé une erreur", - "APP_NAME_support": "Support pour %(APP_NAME)s", - "please_email_questions_to": "Veuillez s'il-vous-plaît envoyer vos questions par email à", - "next_7_strings_single_block": { - "authors_get_paid_when_people_like_you_upvote_their_post": "Les auteurs sont payés lorsque des personnes comme vous votent pour leur contenu", - "if_you_enjoyed_what_you_read_earn_amount": "Si vous avez apprécié ce que vous avez lu ici, créez votre compte dès à présent et commencer par gagner immédiatement du STEEM", - "free_steem": "STEEM GRATUIT!", - "sign_up_earn_steem": "S'enregistrer pour gagner" - }, - "next_3_strings_together": { - "show_more": "Révéler plus", - "show_less": "Révéler moins", - "value_posts": "Articles de faible valeur" - }, - "read_only_mode": "En raison d'une maintenance des serveurs, le site est en mode lecture seule. Nous sommes désolés pour le désagrément. ", - "tags_and_topics": "Mots clés et sujets", - "show_more_topics": "Révéler plus de sujets", - "basic": "Basique", - "advanced": "Avancé", - "views": { - "zero": "Pas de vue", - "one": "%(count)s vue", - "other": "%(count)s vues" - }, - "responses": { - "zero": "Pas de réponse", - "one": "%(count)s réponse", - "other": "%(count)s réponses" - }, - "post_key_warning": { - "confirm": "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", - "warning": "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", - "checkbox": "I understand" - } - }, - "navigation": { - "about": "A propos", - "explore": "Explorer", - "APP_NAME_whitepaper": "%(APP_NAME)s livre blanc", - "buy_LIQUID_TOKEN": "Acheter %(LIQUID_TOKEN)s", - "sell_LIQUID_TOKEN": "Vendre %(LIQUID_TOKEN)s", - "currency_market": "Marché des devises", - "stolen_account_recovery": "Récupération de comptes dérobés", - "change_account_password": "Changement de mot de passe", - "witnesses": "Témoins", - "vote_for_witnesses": "Voter pour les témoins", - "privacy_policy": "Politique de confidentialité", - "terms_of_service": "Conditions d'utilisation", - "sign_up": "Rejoindre", - "learn_more": "En savoir plus", - "welcome": "Bienvenue", - "faq": "FAQ", - "shop": "The Steemit Shop", - "chat": "Salons de discussion Steemit", - "app_center": "Centre d'applications Steemit", - "api_docs": "Documentation API sur Steemit", - "whitepaper": "Livre blanc sur le Steem", - "bluepaper": "Steem Bluepaper", - "smt_whitepaper": "Livre blanc sur le SMT", - "intro_tagline": "Présentations sur la monnaie", - "intro_paragraph": "Votre voix vaut quelque chose. Rejoignez la communauté qui vous rémunère pour vos articles et participez à la curation du contenu de qualité." - }, - "main_menu": { - "hot": "chaud", - "trending": "en vogue" - }, - "reply_editor": { - "shorten_title": "Raccourcir le titre", - "exceeds_maximum_length": "Dépassement de la taille maximale ( %(maxKb)s KB)", - "including_the_category": "(incluant la catégorie '%(rootCategory)s')", - "use_limited_amount_of_tags": "Vous avez un total de %(tagsLength)s mots clés %(includingCategory)s. Veuillez en utiliser seulement 5, autant pour l'article que pour le champ dédié aux mots clés.", - "are_you_sure_you_want_to_clear_this_form": "Etes-vous sûr de vouloir effacer ce formulaire ?", - "uploading": "Téléchargement en cours", - "draft_saved": "Brouillon sauvegardé", - "editor": "Editeur", - "insert_images_by_dragging_dropping": "Insertion d'images par 'drag and drop'", - "pasting_from_the_clipboard": "copie depuis le presse-papier,", - "selecting_them": "les sélectionnant", - "image_upload": "Téléchargement d'image", - "power_up_100": "Conversion en influence à 100%%", - "default_50_50": "Par défaut (50 %%/ 50%%)", - "decline_payout": "Refuser les gains", - "check_this_to_auto_upvote_your_post": "Cocher cette case pour auto-voter pour votre article", - "markdown_styling_guide": "Guide stylistique pour le format Markdown", - "or_by": "ou par", - "title": "Titre", - "update_post": "Mettre l'article à jour", - "markdown_not_supported": "Le format Markdown n'est pas supporté ici" - }, - "category_selector_jsx": { - "tag_your_story": "Mots clés (jusqu'à 5 mots clés), le premier d'entre eux étant votre catégorie principale.", - "select_a_tag": "Sélectionner un mot clé", - "maximum_tag_length_is_24_characters": "La longueur d'un mot clé est d'au maximum 24 caractères", - "use_limited_amount_of_categories": "Veuillez s'il-vous-plaît n'utiliser que %(amount)s catégories", - "use_only_lowercase_letters": "Veuillez n'utiliser que des lettres minuscules", - "use_one_dash": "Veuillez n'utiliser qu'un seul trait d'union", - "use_spaces_to_separate_tags": "Veuillez utiliser des espaces pour séparer les mots clés", - "use_only_allowed_characters": "Veuillez n'utilisez que des lettres minuscules, des chiffres et au plus un trait d'union", - "must_start_with_a_letter": "Le premier caractère doit être une lettre", - "must_end_with_a_letter_or_number": "Le dernier caractère doit être une lettre ou un chiffre" - }, - "postfull_jsx": { - "this_post_is_not_available_due_to_a_copyright_claim": "Cet article n'est pas disponible en raison d'une requête de droits d'auteur.", - "share_on_facebook": "Partager sur Facebook", - "share_on_twitter": "Partager sur Twitter", - "share_on_linkedin": "Partager sur Linkedin", - "recent_password": "Mot de passe antérieur", - "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": "Dans 3.5 jours, conversion de %(amount)s %(DEBT_TOKEN)s en %(LIQUID_TOKEN)s", - "view_the_full_context": "Voir le contexte complet", - "view_the_direct_parent": "Voir le message précédent", - "you_are_viewing_a_single_comments_thread_from": "Vous visualisez un commentaire unique de l'article" - }, - "market_jsx": { - "action": "Action", - "date_created": "Date de création", - "last_price": "Dernier cours", - "24h_volume": "Volume des dernières 24h", - "spread": "Ecart", - "total": "Total", - "available": "Disponible", - "lowest_ask": "Demande au taux le plus bas", - "highest_bid": "Offre au taux le plus haut", - "buy_orders": "Ordres d'achat", - "sell_orders": "Ordres de vente", - "trade_history": "Historique des échanges", - "open_orders": "Ordres ouverts", - "sell_amount_for_atleast": "Vendre %(amount_to_sell)s pour au moins %(min_to_receive)s ( %(effectivePrice)s )", - "buy_atleast_amount_for": "Acheter au moins %(min_to_receive)s pour %(amount_to_sell)s ( %(effectivePrice)s )", - "price_warning_above": "Ce cours est bien au-delà des cours du marché actuels %(marketPrice)s, êtes-vous sûr (e) ?", - "price_warning_below": "Ce cours est bien en-deça des cours du marché actuels %(marketPrice)s, êtes-vous sûr (e) ?", - "order_cancel_confirm": "Annuler l'ordre %(order_id)s de %(user)s", - "order_cancelled": "Ordre %(order_id)s annulé.", - "higher": "Plus haut", - "lower": "Plus bas", - "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" - }, - "recoveraccountstep1_jsx": { - "begin_recovery": "Démarrer la récupération", - "not_valid": "Invalide", - "account_name_is_not_found": "Compte inexistant", - "unable_to_recover_account_not_change_ownership_recently": "Nous sommes incapables de récupérer ce compte, car il n'a pas changé de propriétaire récemment.", - "password_not_used_in_last_days": "Ce mot de passe n'a pas été utilisé durant les derniers 30 jours.", - "request_already_submitted_contact_support": "Votre requête a déjà été soumise et nous nous en occupons. Veuillez s'il-vous-plaît contacter %(SUPPORT_EMAIL)s pour obtenir le statut de votre requête.", - "recover_account_intro": "Il est possible que la clé propriétaire d'un Steemien soit compromise. Le processus de récupération de comptes dérobés est autorisé pour l'utilisateur de droit pendant 30 jours à partir du moment où le voleur a modifié la clé propriétaire. La récupération de comptes dérobés ne peut être utilisée sur %(APP_URL)s que si le propriétaire du compte a ajouté '%(APP_NAME)s'comme mandataire de confiance et accepté les conditions d'utilisation de %(APP_NAME)s.", - "login_with_facebook_or_reddit_media_to_verify_identity": "Veuillez vous identifier via Facebook ou Reddit pour vérifier votre identité.", - "login_with_social_media_to_verify_identity": "Veuillez vous identifier via %(provider)safin de vérifier votre identité", - "enter_email_toverify_identity": "Nous devons vérifier votre identité. Veuillez s'il-vous-plaît entrer votre adresse email ci-dessous pour démarrer le processus.", - "continue_with_email": "Continuer avec l'email.", - "thanks_for_submitting_request_for_account_recovery": "Merci pour la soumission de votre requête concernant la récupération de votre compte %(APP_NAME)sen utilisant une authentification à multi-facteurs basée sur la blockchain. Nous vous répondrons aussi rapidement que possible. Cependant, il se pourrait que notre réponse prennent du temps en raison du gros volume d'emails à traiter. Veuillez vous préparer à vérifier votre identité.", - "recovering_account": "Récupération du compte", - "recover_account": "Récupérer le compte", - "checking_account_owner": "Vérifier le propriétaire du compte", - "sending_recovery_request": "Envoyer une requête de récupération", - "cant_confirm_account_ownership": "Nous ne pouvons confirmer que vous êtes propriétaire de ce compte. Veuillez s'il-vous-plaît vérifier votre mot de passe.", - "account_recovery_request_not_confirmed": "La requête de la récupération du compte n'est pas encore confirmée. Veuillez réessayer plus tard. Merci de votre patience." - }, - "user_profile": { - "unknown_account": "Compte inconnu", - "user_hasnt_made_any_posts_yet": "Il semblerait que %(name)s n'ait pas encore publié d'article.", - "user_hasnt_started_bloggin_yet": "Il semblerait que %(name)s n'ait pas encore commencer à blogger.", - "user_hasnt_followed_anything_yet": "Il semblerait que %(name)s ne soit fan de personne. Dans le cas où %(name)s s'abonnerait au fil d'autres utilisateurs, son flux sera automatiquement rempli avec du nouveau contenu.", - "user_hasnt_had_any_replies_yet": "%(name)s n'a pas encore reçu de réponse", - "looks_like_you_havent_posted_anything_yet": "Looks like you haven't posted anything yet.", - "create_a_post": "Create a Post", - "explore_trending_articles": "Explore Trending Articles", - "read_the_quick_start_guide": "Read The Quick Start Guide", - "browse_the_faq": "Browse The FAQ", - "followers": "Followers", - "this_is_users_reputations_score_it_is_based_on_history_of_votes": "Ceci est le score de réputation de %(name)s. \n\nLe score de réputation est basé sur l'historique des votes reçus par l'utilisateur, et est utilisé pour masquer le contenu de basse qualité.", - "follower_count": { - "zero": "Pas de fans", - "one": "1 fan", - "other": "%(count)s fans" - }, - "followed_count": { - "zero": "N'est fan d'aucun utilisateur", - "one": "fan d'1 personne", - "other": "%(count)s fans" - }, - "post_count": { - "zero": "Pas d'article", - "one": "1 article", - "other": "%(count)s articles" - } - }, - "authorrewards_jsx": { - "estimated_author_rewards_last_week": "Gains de publication estimés pour la semaine dernière", - "author_rewards_history": "Historique des gains de publication" - }, - "curationrewards_jsx": { - "estimated_curation_rewards_last_week": "Gains de curation estimés pour la semaine dernière", - "curation_rewards_history": "Historique des gains de curation" - }, - "post_jsx": { - "now_showing_comments_with_low_ratings": "Affichant à présent les commentaires de faible évaluation", - "sort_order": "Classer les ordres", - "comments_were_hidden_due_to_low_ratings": "Certains commentaires sont masqués en raison de leur faible évaluation" - }, - "voting_jsx": { - "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": "Signaler un article peut diminuer ses gains potentiels et rendre son contenu moins visible. Quelques raisons usuelles pour signaler un article :", - "disagreement_on_rewards": "Désaccord sur les gains", - "fraud_or_plagiarism": "Fraude ou plagiarisme", - "hate_speech_or_internet_trolling": "Discours haineux ou troll", - "intentional_miss_categorized_content_or_spam": "Contenu catégorisé incorrectement de façon intentionnelle ou spam", - "pending_payout": "%(value)s$ de gains en suspens", - "payout_declined": "Gains refusés", - "max_accepted_payout": "%(value)s$ de gains maximum acceptés", - "promotion_cost": "Cout de la promotion: %(value)s$", - "past_payouts": "Gains antérieurs: %(value)s$", - "past_payouts_author": "- Auteur: %(value)s$", - "past_payouts_curators": "Curateurs: %(value)s$", - "we_will_reset_curation_rewards_for_this_post": "cela annulera vos potentiels gains de curation pour cet article", - "removing_your_vote": "Enlever votre vote", - "changing_to_an_upvote": "Modifier pour un vote positif", - "changing_to_a_downvote": "Modifier pour un vote négatif", - "confirm_flag": "Confirmer la signalisation", - "and_more": "et %(count)s en plus", - "votes_plural": { - "one": "%(count)s vote", - "other": "%(count)s votes" - } - }, - "witnesses_jsx": { - "witness_thread": "article introductif pour le témoin", - "top_witnesses": "Votes pour les témoins", - "you_have_votes_remaining": { - "zero": "Il ne vous reste plus de vote disponible", - "one": "Il vous reste 1 vote disponible", - "other": "Il vous reste %(count)s votes disponibles" - }, - "you_can_vote_for_maximum_of_witnesses": "Vous pouvez voter pour un maximum de 30 témoins", - "witness": "Témoin", - "information": "Information", - "if_you_want_to_vote_outside_of_top_enter_account_name": "Si vous voulez voter pour un témoin en dehors du top 50, veuille entrer le nom du compte concerné ci-dessous afin de voter pour lui", - "set_witness_proxy": "Vous pouvez aussi donner une procuration à quelqu'un qui votera pour les témoins en votre nom. Cela annulera votre choix de témoins actuels.", - "witness_set": "Vous avez donné une procuration pour le vote des témoins. Si vous voulez réactiver le vote manuel, veuillez annuler votre procuration.", - "witness_proxy_current": "Vous avez donné une procuration à", - "witness_proxy_set": "Donner une procuration", - "witness_proxy_clear": "Annuler la procuration", - "proxy_update_error": "Votre procuration n'a pas été mise à jour" - }, - "votesandcomments_jsx": { - "no_responses_yet_click_to_respond": "Pas encore de réponse. Cliquez ici pour répondre.", - "response_count_tooltip": { - "zero": "Pas de réponse. Cliquez ici pour répondre.", - "one": "1 réponse. Cliquez ici pour répondre.", - "other": "%(count)s réponses. Cliquez ici pour répondre. " - }, - "vote_count": { - "zero": "Pas de vote", - "one": "1 vote", - "other": "%(count)svotes" + "g": { + "age": "ancienneté", + "amount": "Montant", + "and": "et", + "are_you_sure": "Etes-vous sûr(e) ?", + "ask": "Demande", + "balance": "Solde", + "balances": "Soldes", + "bid": "Offre", + "blog": "Blog", + "browse": "Naviguer", + "buy": "Acheter", + "buy_or_sell": "Acheter ou vendre", + "by": "par", + "cancel": "Annuler", + "change_password": "Changer le mot de passe", + "choose_language": "Choisir la langue", + "clear": "Effacer", + "close": "Fermer", + "collapse_or_expand": "Compacter / Etendre", + "comments": "Commentaires", + "confirm": "Confirmer", + "convert": "Convertir", + "date": "Date", + "delete": "Supprimer", + "dismiss": "Ignorer", + "edit": "Editer", + "email": "Email", + "feed": "Flux", + "follow": "S'abonner", + "for": "pour", + "from": "de", + "go_back": "Retour", + "hide": "Masquer", + "in": "dans", + "in_reply_to": "en réponse à", + "insufficient_balance": "Solde insuffisant", + "invalid_amount": "Montant invalide", + "joined": "Inscrit en", + "loading": "Chargement en cours", + "login": "Se connecter", + "logout": "Déconnexion", + "memo": "Note", + "mute": "Ignorer", + "new": "nouveau", + "newer": "Plus récent", + "next": "Suivant", + "no": "Non", + "ok": "Ok", + "older": "Plus ancien", + "or": "ou", + "order_placed": "Ordre effectué", + "password": "Mot de passe", + "payouts": "Gains", + "permissions": "Permissions", + "phishy_message": + "Link expanded to plain text; beware of a potential phishing attempt", + "post": "Poster", + "post_as": "Poster en tant que", + "posts": "Articles", + "powered_up_100": "Converti en influence à 100%%", + "preview": "Prévisualiser", + "previous": "Précédent", + "price": "Prix", + "print": "Imprimer", + "promote": "Promouvoir", + "promoted": "promu", + "re": "RE", + "re_to": "RE :%(topic)s", + "recent_password": "Mot de passe antérieur", + "receive": "Recevoir", + "remove": "Enlever", + "remove_vote": "Enlever son vote", + "replied_to": "répondu à %(account)s", + "replies": "Réponses", + "reply": "Répondre", + "reply_count": { + "zero": "pas de réponse", + "one": "1 réponse", + "other": "%(count)s réponses" + }, + "reputation": "Réputation", + "reveal_comment": "Révéler le commentaire", + "request": "requête", + "required": "Requis", + "rewards": "Gains", + "save": "Sauvegarder", + "saved": "Sauvegardé", + "search": "Rechercher", + "sell": "Vendre", + "settings": "Configuration", + "share_this_post": "Partager cet article", + "show": "Montrer", + "sign_in": "S'inscrire", + "sign_up": "S'inscrire", + "since": "depuis", + "submit": "Soumettre", + "power_up": "Augmenter son influence", + "submit_a_story": "Poster", + "tag": "Mot clé", + "to": "vers", + "all_tags": "All tags", + "transfer": "Transférer", + "trending_topics": "Sujets en vogue", + "type": "Genre", + "unfollow": "Se désabonner", + "unmute": "Ne plus ignorer", + "unknown": "Inconnu", + "upvote": "Voter pour", + "upvote_post": "Voter pour l'article", + "username": "Identifiant", + "version": "Version", + "vote": "Voter", + "votes": "votes", + "wallet": "Portefeuille", + "warning": "alarme", + "yes": "Oui", + "posting": "de publication", + "owner": "propriétaire", + "active": "active", + "account_not_found": "Compte inconnu", + "this_is_wrong_password": "Mot de passe incorrect", + "do_you_need_to": "Avez-vous besoin de", + "account_name": "Nom du compte", + "recover_your_account": "Récupérer son compte", + "reset_usernames_password": + "Réinitialiser le mot de passe de %(username)s", + "this_will_update_usernames_authtype_key": + "Ceci mettra a jour la clé %(authType)s de %(username)s", + "passwords_do_not_match": "Mots de passe différents", + "you_need_private_password_or_key_not_a_public_key": + "La clé ou le mot de passe privé (et non public) est nécessaire ", + "the_rules_of_APP_NAME": { + "one": + "La première règle de %(APP_NAME)s est de ne pas perdre son mot de passe.", + "second": + "La deuxième règle de %(APP_NAME)s est de ne pas perdre son mot de passe.", + "third": + "La troisième règle de %(APP_NAME)s est que nous ne pouvons réinitialiser votre mot de passe.", + "fourth": + "La quatrième règle est que si vous pouvez vous rappeler de votre mot de passe, il n'est pas assez sécuritaire.", + "fifth": + "La cinquième règle demande d'utiliser uniquement des mots de passe générés aléatoirement.", + "sixth": + "La sixième règle est de ne donner à personne son mot de passe.", + "seventh": + "La septième règle est de faire une sauvegarde de son mot de passe." + }, + "recover_password": "Récupérer un compte", + "current_password": "Mot de passe actuel", + "generated_password": "Mot de passe généré", + "backup_password_by_storing_it": + "A sauvegarder en le copiant dans un fichier texte ou via un logiciel de gestion de mot de passe", + "enter_account_show_password": + "Entrer un identifiant valide pour afficher le mot de passe", + "click_to_generate_password": + "Cliquer ici pour générer un mot de passe", + "re_enter_generate_password": "Recopier le mot de passe généré", + "understand_that_APP_NAME_cannot_recover_password": + "Je comprends que %(APP_NAME)s ne peut récupérer les mots de passe perdus", + "i_saved_password": + "J'ai sauvegardé mon mot de passe de façon sécuritaire", + "update_password": "Mettre à jour le mot de passe", + "confirm_password": "Confirmer le mot de passe", + "account_updated": "Compte mis à jour", + "password_must_be_characters_or_more": + "Le mot de passe doit comporter au moins %(amount)s caractères supplémentaires", + "need_password_or_key": + "Il vous faut une clé ou un mot de passe privé (et non public)", + "login_to_see_memo": "S'identifier pour voir le mémo", + "new_password": "Nouveau mot de passe", + "incorrect_password": "Mot de passe incorrect", + "username_does_not_exist": "Cet identifiant n'existe pas", + "account_name_should_start_with_a_letter": + "L'identifiant doit commencer par une lettre.", + "account_name_should_be_shorter": "L'identifiant doit être plus court.", + "account_name_should_be_longer": "L'identifiant doit être plus long.", + "account_name_should_have_only_letters_digits_or_dashes": + "L'identifiant ne peut comporter que des lettres, des chiffres ou des traits d'union.", + "cannot_increase_reward_of_post_within_the_last_minute_before_payout": + "Les gains d'un article ne peuvent être accrus durant la dernière minute avant rétribution.", + "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": + "un vote existe actuellement; l'utilisateur doit indiquer une raison pour rejeter le témoin", + "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": + "Un seul compte Steem est permis par adresse IP toutes les 10 minutes", + "resteem_this_post": "Resteemer cet article", + "reblog": "Resteem", + "write_your_story": "Ecris ton histoire", + "remember_voting_and_posting_key": + "Se souvenir de la clé de publication et de vote", + "auto_login_question_mark": "Identification automatique", + "hide_private_key": "Masquer la clé privée", + "show_private_key": "Révéler la clé privée", + "login_to_show": "S'identifier pour révéler", + "not_valid_email": "Adresse email invalide", + "thank_you_for_being_an_early_visitor_to_APP_NAME": + "Merci pour être l'un des premiers visiteurs de %(APP_NAME)s. Nous reviendrons vers vous aussi tôt que possible.", + "author_rewards": "Gains éditoriaux", + "curation_rewards": "Gains de curation", + "sorry_your_reddit_account_doesnt_have_enough_karma": + "Désolé, votre compte Reddit ne possède pas de Karma Reddit suffisant pour vous qualifier pour une inscription gratuite. Veuillez s'il-vous-plaît indiquer votre adresse email pour être placé(e) sur liste d'attente.", + "register_with_facebook": "S'enregistrer par Facebook", + "or_click_the_button_below_to_register_with_facebook": + "Ou cliquer sur le bouton ci-dessous pour vous enregistrer par Facebook", + "server_returned_error": "le serveur a renvoyé une erreur", + "APP_NAME_support": "Support pour %(APP_NAME)s", + "please_email_questions_to": + "Veuillez s'il-vous-plaît envoyer vos questions par email à", + "next_7_strings_single_block": { + "authors_get_paid_when_people_like_you_upvote_their_post": + "Les auteurs sont payés lorsque des personnes comme vous votent pour leur contenu", + "if_you_enjoyed_what_you_read_earn_amount": + "Si vous avez apprécié ce que vous avez lu ici, créez votre compte dès à présent et commencer par gagner immédiatement du STEEM", + "free_steem": "STEEM GRATUIT!", + "sign_up_earn_steem": "S'enregistrer pour gagner" + }, + "next_3_strings_together": { + "show_more": "Révéler plus", + "show_less": "Révéler moins", + "value_posts": "Articles de faible valeur" + }, + "read_only_mode": + "En raison d'une maintenance des serveurs, le site est en mode lecture seule. Nous sommes désolés pour le désagrément. ", + "tags_and_topics": "Mots clés et sujets", + "show_more_topics": "Révéler plus de sujets", + "basic": "Basique", + "advanced": "Avancé", + "views": { + "zero": "Pas de vue", + "one": "%(count)s vue", + "other": "%(count)s vues" + }, + "responses": { + "zero": "Pas de réponse", + "one": "%(count)s réponse", + "other": "%(count)s réponses" + }, + "post_key_warning": { + "confirm": + "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", + "warning": + "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", + "checkbox": "I understand" + } + }, + "navigation": { + "about": "A propos", + "explore": "Explorer", + "APP_NAME_whitepaper": "%(APP_NAME)s livre blanc", + "buy_LIQUID_TOKEN": "Acheter %(LIQUID_TOKEN)s", + "sell_LIQUID_TOKEN": "Vendre %(LIQUID_TOKEN)s", + "currency_market": "Marché des devises", + "stolen_account_recovery": "Récupération de comptes dérobés", + "change_account_password": "Changement de mot de passe", + "witnesses": "Témoins", + "vote_for_witnesses": "Voter pour les témoins", + "privacy_policy": "Politique de confidentialité", + "terms_of_service": "Conditions d'utilisation", + "sign_up": "Rejoindre", + "learn_more": "En savoir plus", + "welcome": "Bienvenue", + "faq": "FAQ", + "shop": "The Steemit Shop", + "chat": "Salons de discussion Steemit", + "app_center": "Centre d'applications Steemit", + "api_docs": "Documentation API sur Steemit", + "whitepaper": "Livre blanc sur le Steem", + "bluepaper": "Steem Bluepaper", + "smt_whitepaper": "Livre blanc sur le SMT", + "intro_tagline": "Présentations sur la monnaie", + "intro_paragraph": + "Votre voix vaut quelque chose. Rejoignez la communauté qui vous rémunère pour vos articles et participez à la curation du contenu de qualité." + }, + "main_menu": { + "hot": "chaud", + "trending": "en vogue" + }, + "reply_editor": { + "shorten_title": "Raccourcir le titre", + "exceeds_maximum_length": + "Dépassement de la taille maximale ( %(maxKb)s KB)", + "including_the_category": "(incluant la catégorie '%(rootCategory)s')", + "use_limited_amount_of_tags": + "Vous avez un total de %(tagsLength)s mots clés %(includingCategory)s. Veuillez en utiliser seulement 5, autant pour l'article que pour le champ dédié aux mots clés.", + "are_you_sure_you_want_to_clear_this_form": + "Etes-vous sûr de vouloir effacer ce formulaire ?", + "uploading": "Téléchargement en cours", + "draft_saved": "Brouillon sauvegardé", + "editor": "Editeur", + "insert_images_by_dragging_dropping": + "Insertion d'images par 'drag and drop'", + "pasting_from_the_clipboard": "copie depuis le presse-papier,", + "selecting_them": "les sélectionnant", + "image_upload": "Téléchargement d'image", + "power_up_100": "Conversion en influence à 100%%", + "default_50_50": "Par défaut (50 %%/ 50%%)", + "decline_payout": "Refuser les gains", + "check_this_to_auto_upvote_your_post": + "Cocher cette case pour auto-voter pour votre article", + "markdown_styling_guide": "Guide stylistique pour le format Markdown", + "or_by": "ou par", + "title": "Titre", + "update_post": "Mettre l'article à jour", + "markdown_not_supported": "Le format Markdown n'est pas supporté ici" + }, + "category_selector_jsx": { + "tag_your_story": + "Mots clés (jusqu'à 5 mots clés), le premier d'entre eux étant votre catégorie principale.", + "select_a_tag": "Sélectionner un mot clé", + "maximum_tag_length_is_24_characters": + "La longueur d'un mot clé est d'au maximum 24 caractères", + "use_limited_amount_of_categories": + "Veuillez s'il-vous-plaît n'utiliser que %(amount)s catégories", + "use_only_lowercase_letters": + "Veuillez n'utiliser que des lettres minuscules", + "use_one_dash": "Veuillez n'utiliser qu'un seul trait d'union", + "use_spaces_to_separate_tags": + "Veuillez utiliser des espaces pour séparer les mots clés", + "use_only_allowed_characters": + "Veuillez n'utilisez que des lettres minuscules, des chiffres et au plus un trait d'union", + "must_start_with_a_letter": "Le premier caractère doit être une lettre", + "must_end_with_a_letter_or_number": + "Le dernier caractère doit être une lettre ou un chiffre" + }, + "postfull_jsx": { + "this_post_is_not_available_due_to_a_copyright_claim": + "Cet article n'est pas disponible en raison d'une requête de droits d'auteur.", + "share_on_facebook": "Partager sur Facebook", + "share_on_twitter": "Partager sur Twitter", + "share_on_linkedin": "Partager sur Linkedin", + "recent_password": "Mot de passe antérieur", + "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": + "Dans 3.5 jours, conversion de %(amount)s %(DEBT_TOKEN)s en %(LIQUID_TOKEN)s", + "view_the_full_context": "Voir le contexte complet", + "view_the_direct_parent": "Voir le message précédent", + "you_are_viewing_a_single_comments_thread_from": + "Vous visualisez un commentaire unique de l'article" + }, + "market_jsx": { + "action": "Action", + "date_created": "Date de création", + "last_price": "Dernier cours", + "24h_volume": "Volume des dernières 24h", + "spread": "Ecart", + "total": "Total", + "available": "Disponible", + "lowest_ask": "Demande au taux le plus bas", + "highest_bid": "Offre au taux le plus haut", + "buy_orders": "Ordres d'achat", + "sell_orders": "Ordres de vente", + "trade_history": "Historique des échanges", + "open_orders": "Ordres ouverts", + "sell_amount_for_atleast": + "Vendre %(amount_to_sell)s pour au moins %(min_to_receive)s ( %(effectivePrice)s )", + "buy_atleast_amount_for": + "Acheter au moins %(min_to_receive)s pour %(amount_to_sell)s ( %(effectivePrice)s )", + "price_warning_above": + "Ce cours est bien au-delà des cours du marché actuels %(marketPrice)s, êtes-vous sûr (e) ?", + "price_warning_below": + "Ce cours est bien en-deça des cours du marché actuels %(marketPrice)s, êtes-vous sûr (e) ?", + "order_cancel_confirm": "Annuler l'ordre %(order_id)s de %(user)s", + "order_cancelled": "Ordre %(order_id)s annulé.", + "higher": "Plus haut", + "lower": "Plus bas", + "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": + "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" + }, + "recoveraccountstep1_jsx": { + "begin_recovery": "Démarrer la récupération", + "not_valid": "Invalide", + "account_name_is_not_found": "Compte inexistant", + "unable_to_recover_account_not_change_ownership_recently": + "Nous sommes incapables de récupérer ce compte, car il n'a pas changé de propriétaire récemment.", + "password_not_used_in_last_days": + "Ce mot de passe n'a pas été utilisé durant les derniers 30 jours.", + "request_already_submitted_contact_support": + "Votre requête a déjà été soumise et nous nous en occupons. Veuillez s'il-vous-plaît contacter %(SUPPORT_EMAIL)s pour obtenir le statut de votre requête.", + "recover_account_intro": + "Il est possible que la clé propriétaire d'un Steemien soit compromise. Le processus de récupération de comptes dérobés est autorisé pour l'utilisateur de droit pendant 30 jours à partir du moment où le voleur a modifié la clé propriétaire. La récupération de comptes dérobés ne peut être utilisée sur %(APP_URL)s que si le propriétaire du compte a ajouté '%(APP_NAME)s'comme mandataire de confiance et accepté les conditions d'utilisation de %(APP_NAME)s.", + "login_with_facebook_or_reddit_media_to_verify_identity": + "Veuillez vous identifier via Facebook ou Reddit pour vérifier votre identité.", + "login_with_social_media_to_verify_identity": + "Veuillez vous identifier via %(provider)safin de vérifier votre identité", + "enter_email_toverify_identity": + "Nous devons vérifier votre identité. Veuillez s'il-vous-plaît entrer votre adresse email ci-dessous pour démarrer le processus.", + "continue_with_email": "Continuer avec l'email.", + "thanks_for_submitting_request_for_account_recovery": + "Merci pour la soumission de votre requête concernant la récupération de votre compte %(APP_NAME)sen utilisant une authentification à multi-facteurs basée sur la blockchain. Nous vous répondrons aussi rapidement que possible. Cependant, il se pourrait que notre réponse prennent du temps en raison du gros volume d'emails à traiter. Veuillez vous préparer à vérifier votre identité.", + "recovering_account": "Récupération du compte", + "recover_account": "Récupérer le compte", + "checking_account_owner": "Vérifier le propriétaire du compte", + "sending_recovery_request": "Envoyer une requête de récupération", + "cant_confirm_account_ownership": + "Nous ne pouvons confirmer que vous êtes propriétaire de ce compte. Veuillez s'il-vous-plaît vérifier votre mot de passe.", + "account_recovery_request_not_confirmed": + "La requête de la récupération du compte n'est pas encore confirmée. Veuillez réessayer plus tard. Merci de votre patience." + }, + "user_profile": { + "unknown_account": "Compte inconnu", + "user_hasnt_made_any_posts_yet": + "Il semblerait que %(name)s n'ait pas encore publié d'article.", + "user_hasnt_started_bloggin_yet": + "Il semblerait que %(name)s n'ait pas encore commencer à blogger.", + "user_hasnt_followed_anything_yet": + "Il semblerait que %(name)s ne soit fan de personne. Dans le cas où %(name)s s'abonnerait au fil d'autres utilisateurs, son flux sera automatiquement rempli avec du nouveau contenu.", + "user_hasnt_had_any_replies_yet": + "%(name)s n'a pas encore reçu de réponse", + "looks_like_you_havent_posted_anything_yet": + "Looks like you haven't posted anything yet.", + "create_a_post": "Create a Post", + "explore_trending_articles": "Explore Trending Articles", + "read_the_quick_start_guide": "Read The Quick Start Guide", + "browse_the_faq": "Browse The FAQ", + "followers": "Followers", + "this_is_users_reputations_score_it_is_based_on_history_of_votes": + "Ceci est le score de réputation de %(name)s. \n\nLe score de réputation est basé sur l'historique des votes reçus par l'utilisateur, et est utilisé pour masquer le contenu de basse qualité.", + "follower_count": { + "zero": "Pas de fans", + "one": "1 fan", + "other": "%(count)s fans" + }, + "followed_count": { + "zero": "N'est fan d'aucun utilisateur", + "one": "fan d'1 personne", + "other": "%(count)s fans" + }, + "post_count": { + "zero": "Pas d'article", + "one": "1 article", + "other": "%(count)s articles" + } + }, + "authorrewards_jsx": { + "estimated_author_rewards_last_week": + "Gains de publication estimés pour la semaine dernière", + "author_rewards_history": "Historique des gains de publication" + }, + "curationrewards_jsx": { + "estimated_curation_rewards_last_week": + "Gains de curation estimés pour la semaine dernière", + "curation_rewards_history": "Historique des gains de curation" + }, + "post_jsx": { + "now_showing_comments_with_low_ratings": + "Affichant à présent les commentaires de faible évaluation", + "sort_order": "Classer les ordres", + "comments_were_hidden_due_to_low_ratings": + "Certains commentaires sont masqués en raison de leur faible évaluation" + }, + "voting_jsx": { + "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": + "Signaler un article peut diminuer ses gains potentiels et rendre son contenu moins visible. Quelques raisons usuelles pour signaler un article :", + "disagreement_on_rewards": "Désaccord sur les gains", + "fraud_or_plagiarism": "Fraude ou plagiarisme", + "hate_speech_or_internet_trolling": "Discours haineux ou troll", + "intentional_miss_categorized_content_or_spam": + "Contenu catégorisé incorrectement de façon intentionnelle ou spam", + "pending_payout": "%(value)s$ de gains en suspens", + "payout_declined": "Gains refusés", + "max_accepted_payout": "%(value)s$ de gains maximum acceptés", + "promotion_cost": "Cout de la promotion: %(value)s$", + "past_payouts": "Gains antérieurs: %(value)s$", + "past_payouts_author": "- Auteur: %(value)s$", + "past_payouts_curators": "Curateurs: %(value)s$", + "we_will_reset_curation_rewards_for_this_post": + "cela annulera vos potentiels gains de curation pour cet article", + "removing_your_vote": "Enlever votre vote", + "changing_to_an_upvote": "Modifier pour un vote positif", + "changing_to_a_downvote": "Modifier pour un vote négatif", + "confirm_flag": "Confirmer la signalisation", + "and_more": "et %(count)s en plus", + "votes_plural": { + "one": "%(count)s vote", + "other": "%(count)s votes" + } + }, + "witnesses_jsx": { + "witness_thread": "article introductif pour le témoin", + "top_witnesses": "Votes pour les témoins", + "you_have_votes_remaining": { + "zero": "Il ne vous reste plus de vote disponible", + "one": "Il vous reste 1 vote disponible", + "other": "Il vous reste %(count)s votes disponibles" + }, + "you_can_vote_for_maximum_of_witnesses": + "Vous pouvez voter pour un maximum de 30 témoins", + "witness": "Témoin", + "information": "Information", + "if_you_want_to_vote_outside_of_top_enter_account_name": + "Si vous voulez voter pour un témoin en dehors du top 50, veuille entrer le nom du compte concerné ci-dessous afin de voter pour lui", + "set_witness_proxy": + "Vous pouvez aussi donner une procuration à quelqu'un qui votera pour les témoins en votre nom. Cela annulera votre choix de témoins actuels.", + "witness_set": + "Vous avez donné une procuration pour le vote des témoins. Si vous voulez réactiver le vote manuel, veuillez annuler votre procuration.", + "witness_proxy_current": "Vous avez donné une procuration à", + "witness_proxy_set": "Donner une procuration", + "witness_proxy_clear": "Annuler la procuration", + "proxy_update_error": "Votre procuration n'a pas été mise à jour" + }, + "votesandcomments_jsx": { + "no_responses_yet_click_to_respond": + "Pas encore de réponse. Cliquez ici pour répondre.", + "response_count_tooltip": { + "zero": "Pas de réponse. Cliquez ici pour répondre.", + "one": "1 réponse. Cliquez ici pour répondre.", + "other": "%(count)s réponses. Cliquez ici pour répondre. " + }, + "vote_count": { + "zero": "Pas de vote", + "one": "1 vote", + "other": "%(count)svotes" + } + }, + "userkeys_jsx": { + "public": "Public", + "private": "Privé", + "public_something_key": "Clé publique %(key)s", + "private_something_key": "Clé privée %(key)s", + "posting_key_is_required_it_should_be_different": + "Le clé de publication est utilisée pour publier et voter. Elle devrait être différente des clés active et propriétaire.", + "the_active_key_is_used_to_make_transfers_and_place_orders": + "La clé active est utilisée pour effectuer des transferts et des placer des ordres sur le marché interne.", + "the_owner_key_is_required_to_change_other_keys": + "La clé propriétaire est la clé principale du compte et est requise pour changer les autres clés.", + "the_private_key_or_password_should_be_kept_offline": + "La clé privée, ou le mot de passe pour la clé propriétaire, devrait être gardée hors ligne autant que possible.", + "the_memo_key_is_used_to_create_and_read_memos": + "La clé pour les notes est utilisée pour créer et lire des notes." + }, + "suggestpassword_jsx": { + "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": + "%(APP_NAME)s ne peut pas récupérer de mots de passe. Veuille garder une trace de cette page en lieu sûr, comme un coffre fort ininflammable et sécuritaire.", + "APP_NAME_password_backup": + "Sauvegarde de mot de passe pour %(APP_NAME)s", + "APP_NAME_password_backup_required": + "Sauvegarde de mot de passe pour %(APP_NAME)s (requis)", + "after_printing_write_down_your_user_name": + "Après impression, veuillez indiquer votre identifiant" + }, + "converttosteem_jsx": { + "your_existing_DEBT_TOKEN_are_liquid_and_transferable": + "Vos %(DEBT_TOKEN)s existants sont en liquide et donc transférable. Vous pouvez par exemple ėchanger %(DEBT_TOKEN)s directement sur ce site via le lien %(link)s, ou les transférer vers un marché externe.", + "this_is_a_price_feed_conversion": + "Il s'agit d'une conversion au taux du marché. Le délais de 3.5 jours est nécessaire afin d'éviter les abus de ceux qui tenterait de parier sur les modifications à court terme du taux du marché moyen.", + "convert_to_LIQUID_TOKEN": "Convertir en %(LIQUID_TOKEN)s", + "DEBT_TOKEN_will_be_unavailable": + "Cette action aura lieu dans 3.5 jours et ne peut être annulée. Ces %(DEBT_TOKEN)s deviendront immédiatement non disponibles." + }, + "tips_js": { + "liquid_token": + "Des jetons négociables peuvent être transférés n'importe où à tout moment.
        %(LIQUID_TOKEN)s peuvent être convertis en %(VESTING_TOKEN)s lors d'un processus appelé conversion en influence, ou power up.", + "influence_token": + "Jetons d'influence qui vous donnent plus de contrôle sur les gains potentiels des articles et vous permettent d'augmenter vos gains de curation", + "estimated_value": + "La valeur estimée est basée sur la valeur moyenne de %(LIQUID_TOKEN)s en dollars américains.", + "non_transferable": + "%(VESTING_TOKEN)s est non transférable et demande 3 mois (13 paiements) pour être convertis en %(LIQUID_TOKEN)s. ", + "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": + "Les %(VESTING_TOKEN)s convertis peuvent être envoyés soit à vous, soit à quelqu'un d'autre, mais ne peuvent pas être transférés à nouveau sans être convertis en %(LIQUID_TOKEN)s.", + "part_of_your_steem_power_is_currently_delegated": + "Une partie de votre influence, ou STEEM POWER, est actuellement déléguée. La délégation est un don pour augmenter l'influence ou pour encourager les nouveaux utilisateurs à effectuer des actions sur Steemit. Le montant délégué peut varier." + }, + "promote_post_jsx": { + "promote_post": "Promouvoir un article", + "spend_your_DEBT_TOKEN_to_advertise_this_post": + "Dépenser vos %(DEBT_TOKEN)s pour afficher cet article dans la section contenant les articles promus. ", + "you_successfully_promoted_this_post": + "Vous avez promotionné cet article avec succès", + "this_post_was_hidden_due_to_low_ratings": + "Cet article est masqué en raison d'une faible évaluation." + }, + "about_jsx": { + "about_app": "A propos de %(APP_NAME)s", + "about_app_details": + "%(APP_NAME)s est une plateforme de réseau social où les utilisateurs sont payés pour créer et effecter la curation du contenu. Elle influence un système de points digitaux, appelé Steem, qui ont une valeur réelle en fonction des prix du marché et des liquidités disponibles.", + "learn_more_at_app_url": "Plus d'informations à %(APP_URL)s", + "resources": "Ressources" + }, + "markdownviewer_jsx": { + "images_were_hidden_due_to_low_ratings": + "Les images sont masqués en raison d'une faible évaluation." + }, + "postsummary_jsx": { + "resteemed": "resteemé", + "resteemed_by": "Resteemé par", + "reveal_it": "Révélez-le", + "adjust_your": "Ajustez vos", + "display_preferences": "préférences visuelles", + "create_an_account": "créer un compte", + "to_save_your_preferences": "Pour enregistrer vos préférences" + }, + "posts_index": { + "empty_feed_1": + "Il semblerait que vous ne vous êtes abonné(e) à aucun fil pour le moment", + "empty_feed_2": + "Si vous vous êtes récemment abonné(e) au fil de nouveaux utilisateurs, votre flux personnel sera automatiquement rempli lorsque du nouveau contenu apparaîtra", + "empty_feed_3": "Explorer les articles en vogue", + "empty_feed_4": "Lire le guide de prise en main rapide", + "empty_feed_5": "Parcourir la FAQ" + }, + "transferhistoryrow_jsx": { + "to_savings": "vers l'épargne", + "from_savings": "depuis l'épargne", + "cancel_transfer_from_savings": "Annuler le transfert vers l'épargne", + "stop_power_down": + "Arrêter la récupération de l'influence, ou le power down", + "start_power_down_of": + "Démarrer la récupération de l'influence, ou le power down", + "receive_interest_of": "Recevoir les intérêts de" + }, + "savingswithdrawhistory_jsx": { + "cancel_this_withdraw_request": + "Voulez-vous annuler cette demande de retrait ?", + "pending_savings_withdrawals": "RETRAITS D'EPARGNE EN SUSPENS", + "withdraw": "Retirer %(amount)s", + "to": "vers %(to)s", + "from_to": "de %(from)s vers %(to)s" + }, + "explorepost_jsx": { + "copied": "Copié!", + "copy": "COPIER", + "alternative_sources": "Références alternatives" + }, + "header_jsx": { + "home": "répertoire personnel", + "create_a_post": "Créer un article", + "change_account_password": "Changer le mot de passe du compte", + "create_account": "Créer un compte", + "stolen_account_recovery": "Récupération des comptes dérobés", + "people_following": "Personnes fans de", + "people_followed_by": "Personnes suivies par", + "curation_rewards_by": "Gains de curation de", + "author_rewards_by": "Gains de publication de", + "replies_to": "Réponses à", + "comments_by": "Commentaires de" + }, + "loginform_jsx": { + "you_need_a_private_password_or_key": + "Il vous faut un mot de passe ou une clé privé (pas une clé publique)", + "cryptography_test_failed": "Le test de cryptographie a échoué", + "unable_to_log_you_in": + "Nous sommes incapables de vous identifier avec ce navigateur.", + "the_latest_versions_of": "Les dernières versions de", + "are_well_tested_and_known_to_work_with": + "sont bien testées et connues pour fonctionner avec %(APP_URL)s.", + "due_to_server_maintenance": + "En raison de la maintenance des serveurs, nous opérons en mode 'lecture seule'. Nous nous excusons des désagréments. ", + "login_to_vote": "S'identifier pour voter", + "login_to_post": "S'identifier pour publier", + "login_to_comment": "S'identifier pour commenter", + "posting": "de publication", + "active_or_owner": "Active ou propriétaire", + "this_password_is_bound_to_your_account_owner_key": + "Ce mot de passe est lié à votre clé propriétaire et ne peut pas être utilisé pour vous identifier sur ce site.", + "however_you_can_use_it_to": "Cependant, vous pouvez l'utiliser pour ", + "update_your_password": "Mettre à jour votre mot de passe", + "to_obtain_a_more_secure_set_of_keys": + "Pour obtenir un jeu de clés plus sûr.", + "this_password_is_bound_to_your_account_active_key": + "Ce mot de passe est lié à la clé active de votre compte et ne peut pas être utilisé pour vous connecter à cette page.", + "you_may_use_this_active_key_on_other_more": + "Vous pouvez utiliser cette clé active sur d'autres pages plus sécurisées comme les pages 'Portefeuille' ou 'Marché'.", + "you_account_has_been_successfully_created": + "Votre compte a été créé avec succès!", + "you_account_has_been_successfully_recovered": + "Votre compte a été récupéré avec succès!", + "password_update_succes": + "Le mot de passe pour%(accountName)sa été mis à jour avec succès.", + "password_info": + "Ce mot de passe ou clé privée est incorrect. Il y a probablement une erreur d'écriture ou d'entrée des données. Indication : un mot de passe ou une clé privée générée par Steemit ne contiendra jamais aucun 0 (zéro), O (o majuscule), I (i majuscule) ou l (l minuscule).", + "enter_your_username": "Entrez votre nom d'utilisateur", + "password_or_wif": "Mot de passe ou WIF", + "this_operation_requires_your_key_or_master_password": + "Cette opération nécessite votre clé %(authType)s ou votre mot de passe principal.", + "keep_me_logged_in": "Gardez-moi connecté", + "amazing_community": "communauté extraordinaire", + "to_comment_and_reward_others": + " pour commenter et récompenser les autres.", + "sign_up_get_steem": "Sign up. Get STEEM", + "signup_button": "Sign up now to earn ", + "signup_button_emphasis": "FREE STEEM!", + "returning_users": "Utilisateurs de retour :", + "join_our": "Rejoignez notre" + }, + "chainvalidation_js": { + "account_name_should": "Le nom du compte doit ", + "not_be_empty": "ne peut pas être vide.", + "be_longer": "être plus long.", + "be_shorter": "être plus court.", + "each_account_segment_should": "Chaque segment de compte devrait", + "start_with_a_letter": "commencer par une lettre.", + "have_only_letters_digits_or_dashes": + "ne comporter que des lettres, des chiffres ou des traits d'union.", + "have_only_one_dash_in_a_row": "n'avoir qu'un seul trait d'union.", + "end_with_a_letter_or_digit": "terminer par une lettre ou un chiffre.", + "verified_exchange_no_memo": + "Vous devez inclure un mémo pour votre transfert vers un marché d'échanges." + }, + "settings_jsx": { + "invalid_url": "L'URL est invalide", + "name_is_too_long": "Le nom est trop long", + "name_must_not_begin_with": "Le nom ne doit pas commencer avec un @", + "about_is_too_long": "Le \"A propos\" est trop long", + "location_is_too_long": "Le lieu est trop long", + "website_url_is_too_long": "L'URL du site web est trop longue", + "public_profile_settings": "Paramètres du profil public", + "private_post_display_settings": + "Paramètres de l'affichage des messages privés", + "not_safe_for_work_nsfw_content": + "Peu approprié pour le travail (NSFW)", + "always_hide": "Toujours masquer", + "always_warn": "Toujours avertir", + "always_show": "Toujours montrer", + "muted_users": "Utilisateurs ignorés", + "update": "Mise à jour", + "profile_image_url": "URL de la photo de profil", + "cover_image_url": "URL de l'image de couverture", + "profile_name": "Nom à afficher", + "profile_about": "A propos", + "profile_location": "Lieu", + "profile_website": "Site internet" + }, + "transfer_jsx": { + "amount_is_in_form": "Montant dans le format 99999.999", + "insufficient_funds": "Fonds insuffisants", + "use_only_3_digits_of_precison": + "Utiliser au maximum 3 décimales pour la précision", + "send_to_account": "Envoyer au compte", + "asset": "Actif", + "this_memo_is_private": "Ce mémo est privé", + "this_memo_is_public": "Ce mémo est public", + "convert_to_VESTING_TOKEN": "Convertir en %(VESTING_TOKEN)s", + "balance_subject_to_3_day_withdraw_waiting_period": + "Le solde dépend de la période de 3 jours d'attente requise pour les retraits", + "move_funds_to_another_account": + "Transférer des fonds vers un autre compte %(APP_NAME)s ", + "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": + "Protéger les fonds en imposant une période de latence de 3 jours pour les retraits.", + "withdraw_funds_after_the_required_3_day_waiting_period": + "Retirer les fonds après la période de 3 jours de latence requise", + "from": "De", + "to": "A", + "asset_currently_collecting": + "%(asset)s collectant actuellement %(interest)s %% d'APR.", + "beware_of_spam_and_phishing_links": + "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." + }, + "userwallet_jsx": { + "conversion_complete_tip": "S'achèvera le", + "in_conversion": "%(amount)s en conversion", + "transfer_to_savings": "Transfert vers l'épargne", + "power_up": "Augmenter son influence", + "power_down": "Diminuer son influence", + "market": "Marché", + "convert_to_LIQUID_TOKEN": "Convertir en %(LIQUID_TOKEN)s", + "withdraw_LIQUID_TOKEN": "Retirer %(LIQUID_TOKEN)s", + "withdraw_DEBT_TOKENS": "Retirer %(DEBT_TOKENS)s", + "tokens_worth_about_1_of_LIQUID_TICKER": + "Des jetons d'environ 1.00$ de %(LIQUID_TICKER)s, actuellement donnant lieu à %(sbdInterest)s %% d'APR.", + "savings": "EPARGNE", + "estimated_account_value": "Valeur estimée du compte", + "next_power_down_is_scheduled_to_happen": + "Le prochain retrait d'influence, ou power down, est prévu dans ", + "transfers_are_temporary_disabled": + "Les transferts sont temporairement désactivés.", + "history": "HISTORIQUE", + "redeem_rewards": "Réclamer les gains (transférer vers le solde)", + "buy_steem_or_steem_power": "Acheter du STEEM ou du STEEM POWER" + }, + "powerdown_jsx": { + "power_down": "Diminuer son influence", + "amount": "Montant", + "already_power_down": + "Vous êtes actuellement en train de retirer %(AMOUNT)s %(LIQUID_TICKER)s d'influence (%(WITHDRAWN)s %(LIQUID_TICKER)s déjà payé). Veuillez noter que si le montant retiré est modifié, le compteur pour le prochain retrait sera remis à zéro également.", + "delegating": + "Vous déléguez actuellement %(AMOUNT)s %(LIQUID_TICKER)s. Ce montant est bloqué et est indisponible pour tout retrait d'influence, ou power down, tant que la délégation n'est pas annulée et qu'une période complète de gains soit passée.", + "per_week": "C'est environ %(AMOUNT)s%(LIQUID_TICKER)s par semaine.", + "warning": + "Laisser moins de%(AMOUNT)s%(VESTING_TOKEN)s sur votre compte n'est pas recommandé et peut rendre votre compte inutilisable.", + "error": + "Impossible d'effectuer un retrait d'influence (ERREUR : %(MESSAGE)s)" + }, + "checkloginowner_jsx": { + "your_password_permissions_were_reduced": + "Les permissions de votre mot de passe ont été réduites", + "if_you_did_not_make_this_change": + "Si vous n'avez pas fait ce changement, veuillez s'il-vous-plaît", + "ownership_changed_on": "Changement de propriétaire à compter du ", + "deadline_for_recovery_is": "La date limite de récupération est", + "i_understand_dont_show_again": + "J'ai compris, ne me le montrez plus jamais" } - }, - "userkeys_jsx": { - "public": "Public", - "private": "Privé", - "public_something_key": "Clé publique %(key)s", - "private_something_key": "Clé privée %(key)s", - "posting_key_is_required_it_should_be_different": "Le clé de publication est utilisée pour publier et voter. Elle devrait être différente des clés active et propriétaire.", - "the_active_key_is_used_to_make_transfers_and_place_orders": "La clé active est utilisée pour effectuer des transferts et des placer des ordres sur le marché interne.", - "the_owner_key_is_required_to_change_other_keys": "La clé propriétaire est la clé principale du compte et est requise pour changer les autres clés.", - "the_private_key_or_password_should_be_kept_offline": "La clé privée, ou le mot de passe pour la clé propriétaire, devrait être gardée hors ligne autant que possible.", - "the_memo_key_is_used_to_create_and_read_memos": "La clé pour les notes est utilisée pour créer et lire des notes." - }, - "suggestpassword_jsx": { - "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": "%(APP_NAME)s ne peut pas récupérer de mots de passe. Veuille garder une trace de cette page en lieu sûr, comme un coffre fort ininflammable et sécuritaire.", - "APP_NAME_password_backup": "Sauvegarde de mot de passe pour %(APP_NAME)s", - "APP_NAME_password_backup_required": "Sauvegarde de mot de passe pour %(APP_NAME)s (requis)", - "after_printing_write_down_your_user_name": "Après impression, veuillez indiquer votre identifiant" - }, - "converttosteem_jsx": { - "your_existing_DEBT_TOKEN_are_liquid_and_transferable": "Vos %(DEBT_TOKEN)s existants sont en liquide et donc transférable. Vous pouvez par exemple ėchanger %(DEBT_TOKEN)s directement sur ce site via le lien %(link)s, ou les transférer vers un marché externe.", - "this_is_a_price_feed_conversion": "Il s'agit d'une conversion au taux du marché. Le délais de 3.5 jours est nécessaire afin d'éviter les abus de ceux qui tenterait de parier sur les modifications à court terme du taux du marché moyen.", - "convert_to_LIQUID_TOKEN": "Convertir en %(LIQUID_TOKEN)s", - "DEBT_TOKEN_will_be_unavailable": "Cette action aura lieu dans 3.5 jours et ne peut être annulée. Ces %(DEBT_TOKEN)s deviendront immédiatement non disponibles." - }, - "tips_js": { - "liquid_token": "Des jetons négociables peuvent être transférés n'importe où à tout moment.
        %(LIQUID_TOKEN)s peuvent être convertis en %(VESTING_TOKEN)s lors d'un processus appelé conversion en influence, ou power up.", - "influence_token": "Jetons d'influence qui vous donnent plus de contrôle sur les gains potentiels des articles et vous permettent d'augmenter vos gains de curation", - "estimated_value": "La valeur estimée est basée sur la valeur moyenne de %(LIQUID_TOKEN)s en dollars américains.", - "non_transferable": "%(VESTING_TOKEN)s est non transférable et demande 3 mois (13 paiements) pour être convertis en %(LIQUID_TOKEN)s. ", - "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": "Les %(VESTING_TOKEN)s convertis peuvent être envoyés soit à vous, soit à quelqu'un d'autre, mais ne peuvent pas être transférés à nouveau sans être convertis en %(LIQUID_TOKEN)s.", - "part_of_your_steem_power_is_currently_delegated": "Une partie de votre influence, ou STEEM POWER, est actuellement déléguée. La délégation est un don pour augmenter l'influence ou pour encourager les nouveaux utilisateurs à effectuer des actions sur Steemit. Le montant délégué peut varier." - }, - "promote_post_jsx": { - "promote_post": "Promouvoir un article", - "spend_your_DEBT_TOKEN_to_advertise_this_post": "Dépenser vos %(DEBT_TOKEN)s pour afficher cet article dans la section contenant les articles promus. ", - "you_successfully_promoted_this_post": "Vous avez promotionné cet article avec succès", - "this_post_was_hidden_due_to_low_ratings": "Cet article est masqué en raison d'une faible évaluation." - }, - "about_jsx": { - "about_app": "A propos de %(APP_NAME)s", - "about_app_details": "%(APP_NAME)s est une plateforme de réseau social où les utilisateurs sont payés pour créer et effecter la curation du contenu. Elle influence un système de points digitaux, appelé Steem, qui ont une valeur réelle en fonction des prix du marché et des liquidités disponibles.", - "learn_more_at_app_url": "Plus d'informations à %(APP_URL)s", - "resources": "Ressources" - }, - "markdownviewer_jsx": { - "images_were_hidden_due_to_low_ratings": "Les images sont masqués en raison d'une faible évaluation." - }, - "postsummary_jsx": { - "resteemed": "resteemé", - "resteemed_by": "Resteemé par", - "reveal_it": "Révélez-le", - "adjust_your": "Ajustez vos", - "display_preferences": "préférences visuelles", - "create_an_account": "créer un compte", - "to_save_your_preferences": "Pour enregistrer vos préférences" - }, - "posts_index": { - "empty_feed_1": "Il semblerait que vous ne vous êtes abonné(e) à aucun fil pour le moment", - "empty_feed_2": "Si vous vous êtes récemment abonné(e) au fil de nouveaux utilisateurs, votre flux personnel sera automatiquement rempli lorsque du nouveau contenu apparaîtra", - "empty_feed_3": "Explorer les articles en vogue", - "empty_feed_4": "Lire le guide de prise en main rapide", - "empty_feed_5": "Parcourir la FAQ" - }, - "transferhistoryrow_jsx": { - "to_savings": "vers l'épargne", - "from_savings": "depuis l'épargne", - "cancel_transfer_from_savings": "Annuler le transfert vers l'épargne", - "stop_power_down": "Arrêter la récupération de l'influence, ou le power down", - "start_power_down_of": "Démarrer la récupération de l'influence, ou le power down", - "receive_interest_of": "Recevoir les intérêts de" - }, - "savingswithdrawhistory_jsx": { - "cancel_this_withdraw_request": "Voulez-vous annuler cette demande de retrait ?", - "pending_savings_withdrawals": "RETRAITS D'EPARGNE EN SUSPENS", - "withdraw": "Retirer %(amount)s", - "to": "vers %(to)s", - "from_to": "de %(from)s vers %(to)s" - }, - "explorepost_jsx": { - "copied": "Copié!", - "copy": "COPIER", - "alternative_sources": "Références alternatives" - }, - "header_jsx": { - "home": "répertoire personnel", - "create_a_post": "Créer un article", - "change_account_password": "Changer le mot de passe du compte", - "create_account": "Créer un compte", - "stolen_account_recovery": "Récupération des comptes dérobés", - "people_following": "Personnes fans de", - "people_followed_by": "Personnes suivies par", - "curation_rewards_by": "Gains de curation de", - "author_rewards_by": "Gains de publication de", - "replies_to": "Réponses à", - "comments_by": "Commentaires de" - }, - "loginform_jsx": { - "you_need_a_private_password_or_key": "Il vous faut un mot de passe ou une clé privé (pas une clé publique)", - "cryptography_test_failed": "Le test de cryptographie a échoué", - "unable_to_log_you_in": "Nous sommes incapables de vous identifier avec ce navigateur.", - "the_latest_versions_of": "Les dernières versions de", - "are_well_tested_and_known_to_work_with": "sont bien testées et connues pour fonctionner avec %(APP_URL)s.", - "due_to_server_maintenance": "En raison de la maintenance des serveurs, nous opérons en mode 'lecture seule'. Nous nous excusons des désagréments. ", - "login_to_vote": "S'identifier pour voter", - "login_to_post": "S'identifier pour publier", - "login_to_comment": "S'identifier pour commenter", - "posting": "de publication", - "active_or_owner": "Active ou propriétaire", - "this_password_is_bound_to_your_account_owner_key": "Ce mot de passe est lié à votre clé propriétaire et ne peut pas être utilisé pour vous identifier sur ce site.", - "however_you_can_use_it_to": "Cependant, vous pouvez l'utiliser pour ", - "update_your_password": "Mettre à jour votre mot de passe", - "to_obtain_a_more_secure_set_of_keys": "Pour obtenir un jeu de clés plus sûr.", - "this_password_is_bound_to_your_account_active_key": "Ce mot de passe est lié à la clé active de votre compte et ne peut pas être utilisé pour vous connecter à cette page.", - "you_may_use_this_active_key_on_other_more": "Vous pouvez utiliser cette clé active sur d'autres pages plus sécurisées comme les pages 'Portefeuille' ou 'Marché'.", - "you_account_has_been_successfully_created": "Votre compte a été créé avec succès!", - "you_account_has_been_successfully_recovered": "Votre compte a été récupéré avec succès!", - "password_update_succes": "Le mot de passe pour%(accountName)sa été mis à jour avec succès.", - "password_info": "Ce mot de passe ou clé privée est incorrect. Il y a probablement une erreur d'écriture ou d'entrée des données. Indication : un mot de passe ou une clé privée générée par Steemit ne contiendra jamais aucun 0 (zéro), O (o majuscule), I (i majuscule) ou l (l minuscule).", - "enter_your_username": "Entrez votre nom d'utilisateur", - "password_or_wif": "Mot de passe ou WIF", - "this_operation_requires_your_key_or_master_password": "Cette opération nécessite votre clé %(authType)s ou votre mot de passe principal.", - "keep_me_logged_in": "Gardez-moi connecté", - "amazing_community": "communauté extraordinaire", - "to_comment_and_reward_others": " pour commenter et récompenser les autres.", - "sign_up_get_steem": "Sign up. Get STEEM", - "signup_button": "Sign up now to earn ", - "signup_button_emphasis": "FREE STEEM!", - "returning_users": "Utilisateurs de retour :", - "join_our": "Rejoignez notre" - }, - "chainvalidation_js": { - "account_name_should": "Le nom du compte doit ", - "not_be_empty": "ne peut pas être vide.", - "be_longer": "être plus long.", - "be_shorter": "être plus court.", - "each_account_segment_should": "Chaque segment de compte devrait", - "start_with_a_letter": "commencer par une lettre.", - "have_only_letters_digits_or_dashes": "ne comporter que des lettres, des chiffres ou des traits d'union.", - "have_only_one_dash_in_a_row": "n'avoir qu'un seul trait d'union.", - "end_with_a_letter_or_digit": "terminer par une lettre ou un chiffre.", - "verified_exchange_no_memo": "Vous devez inclure un mémo pour votre transfert vers un marché d'échanges." - }, - "settings_jsx": { - "invalid_url": "L'URL est invalide", - "name_is_too_long": "Le nom est trop long", - "name_must_not_begin_with": "Le nom ne doit pas commencer avec un @", - "about_is_too_long": "Le \"A propos\" est trop long", - "location_is_too_long": "Le lieu est trop long", - "website_url_is_too_long": "L'URL du site web est trop longue", - "public_profile_settings": "Paramètres du profil public", - "private_post_display_settings": "Paramètres de l'affichage des messages privés", - "not_safe_for_work_nsfw_content": "Peu approprié pour le travail (NSFW)", - "always_hide": "Toujours masquer", - "always_warn": "Toujours avertir", - "always_show": "Toujours montrer", - "muted_users": "Utilisateurs ignorés", - "update": "Mise à jour", - "profile_image_url": "URL de la photo de profil", - "cover_image_url": "URL de l'image de couverture", - "profile_name": "Nom à afficher", - "profile_about": "A propos", - "profile_location": "Lieu", - "profile_website": "Site internet" - }, - "transfer_jsx": { - "amount_is_in_form": "Montant dans le format 99999.999", - "insufficient_funds": "Fonds insuffisants", - "use_only_3_digits_of_precison": "Utiliser au maximum 3 décimales pour la précision", - "send_to_account": "Envoyer au compte", - "asset": "Actif", - "this_memo_is_private": "Ce mémo est privé", - "this_memo_is_public": "Ce mémo est public", - "convert_to_VESTING_TOKEN": "Convertir en %(VESTING_TOKEN)s", - "balance_subject_to_3_day_withdraw_waiting_period": "Le solde dépend de la période de 3 jours d'attente requise pour les retraits", - "move_funds_to_another_account": "Transférer des fonds vers un autre compte %(APP_NAME)s ", - "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": "Protéger les fonds en imposant une période de latence de 3 jours pour les retraits.", - "withdraw_funds_after_the_required_3_day_waiting_period": "Retirer les fonds après la période de 3 jours de latence requise", - "from": "De", - "to": "A", - "asset_currently_collecting": "%(asset)s collectant actuellement %(interest)s %% d'APR.", - "beware_of_spam_and_phishing_links": "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." - }, - "userwallet_jsx": { - "conversion_complete_tip": "S'achèvera le", - "in_conversion": "%(amount)s en conversion", - "transfer_to_savings": "Transfert vers l'épargne", - "power_up": "Augmenter son influence", - "power_down": "Diminuer son influence", - "market": "Marché", - "convert_to_LIQUID_TOKEN": "Convertir en %(LIQUID_TOKEN)s", - "withdraw_LIQUID_TOKEN": "Retirer %(LIQUID_TOKEN)s", - "withdraw_DEBT_TOKENS": "Retirer %(DEBT_TOKENS)s", - "tokens_worth_about_1_of_LIQUID_TICKER": "Des jetons d'environ 1.00$ de %(LIQUID_TICKER)s, actuellement donnant lieu à %(sbdInterest)s %% d'APR.", - "savings": "EPARGNE", - "estimated_account_value": "Valeur estimée du compte", - "next_power_down_is_scheduled_to_happen": "Le prochain retrait d'influence, ou power down, est prévu dans ", - "transfers_are_temporary_disabled": "Les transferts sont temporairement désactivés.", - "history": "HISTORIQUE", - "redeem_rewards": "Réclamer les gains (transférer vers le solde)", - "buy_steem_or_steem_power": "Acheter du STEEM ou du STEEM POWER" - }, - "powerdown_jsx": { - "power_down": "Diminuer son influence", - "amount": "Montant", - "already_power_down": "Vous êtes actuellement en train de retirer %(AMOUNT)s %(LIQUID_TICKER)s d'influence (%(WITHDRAWN)s %(LIQUID_TICKER)s déjà payé). Veuillez noter que si le montant retiré est modifié, le compteur pour le prochain retrait sera remis à zéro également.", - "delegating": "Vous déléguez actuellement %(AMOUNT)s %(LIQUID_TICKER)s. Ce montant est bloqué et est indisponible pour tout retrait d'influence, ou power down, tant que la délégation n'est pas annulée et qu'une période complète de gains soit passée.", - "per_week": "C'est environ %(AMOUNT)s%(LIQUID_TICKER)s par semaine.", - "warning": "Laisser moins de%(AMOUNT)s%(VESTING_TOKEN)s sur votre compte n'est pas recommandé et peut rendre votre compte inutilisable.", - "error": "Impossible d'effectuer un retrait d'influence (ERREUR : %(MESSAGE)s)" - }, - "checkloginowner_jsx": { - "your_password_permissions_were_reduced": "Les permissions de votre mot de passe ont été réduites", - "if_you_did_not_make_this_change": "Si vous n'avez pas fait ce changement, veuillez s'il-vous-plaît", - "ownership_changed_on": "Changement de propriétaire à compter du ", - "deadline_for_recovery_is": "La date limite de récupération est", - "i_understand_dont_show_again": "J'ai compris, ne me le montrez plus jamais" - } } diff --git a/src/app/locales/it.json b/src/app/locales/it.json index 8c6b386b2..b469e5b89 100644 --- a/src/app/locales/it.json +++ b/src/app/locales/it.json @@ -1,649 +1,783 @@ { - "g": { - "age": "età", - "amount": "Quantità", - "and": "e", - "are_you_sure": "Sei sicuro?", - "ask": "Ask", - "balance": "Saldo", - "balances": "Saldi", - "bid": "Bid", - "blog": "Blog", - "browse": "Naviga", - "buy": "Acquista", - "buy_or_sell": "Acquista o vendi", - "by": "da", - "cancel": "Cancella", - "change_password": "Cambia Password", - "choose_language": "Scegli la lingua", - "clear": "Pulisci", - "close": "Chiudi", - "collapse_or_expand": "Riduci/Espandi", - "comments": "Commenti", - "confirm": "Conferma", - "convert": "Converti", - "date": "Data", - "delete": "Elimina", - "dismiss": "Abbandona", - "edit": "Modifica", - "email": "Email", - "feed": "Feed", - "follow": "Segui", - "for": "per", - "from": "da", - "go_back": "Indietro", - "hide": "Nascondi", - "in": "in", - "in_reply_to": "in risposta a", - "insufficient_balance": "Credito insufficiente", - "invalid_amount": "Importo errato", - "joined": "Connesso", - "loading": "Caricamento", - "login": "Login", - "logout": "Logout", - "memo": "Memo", - "mute": "Silenzia", - "new": "Nuovo", - "newer": "Più recente", - "next": "Prossimo", - "no": "No", - "ok": "Ok", - "older": "Meno recente", - "or": "o", - "order_placed": "Ordine piazzato", - "password": "Password", - "payouts": "Pagamenti", - "permissions": "Permessi", - "phishy_message": "Link expanded to plain text; beware of a potential phishing attempt", - "post": "Post", - "post_as": "Pubblica come", - "posts": "Posts", - "powered_up_100": "Powered Up 100%%", - "preview": "Anteprima", - "previous": "Precedente", - "price": "Prezzo", - "print": "Stampa", - "promote": "Promuovi", - "promoted": "promosso", - "re": "RE", - "re_to": "RE: %(topic)s", - "recent_password": "Password Recente", - "receive": "Ricevi", - "remove": "Rimuovi", - "remove_vote": "Rimuovi il voto", - "replied_to": "risposto a %(account)s", - "replies": "Risponde", - "reply": "Risposta", - "reply_count": { - "zero": "Nessuna risposta", - "one": "1 risposta", - "other": "%(count)s risposte" - }, - "reputation": "Reputazione", - "reveal_comment": "Mostra Commento", - "request": "richiesta", - "required": "Richiesto", - "rewards": "Ricompense", - "save": "Salva", - "saved": "Salvato", - "search": "Cerca", - "sell": "Vendi", - "settings": "Impostazioni", - "share_this_post": "Condividi questo post", - "show": "Mostra", - "sign_in": "Accedi", - "sign_up": "Registrati", - "since": "da", - "submit": "Sottoscrivi", - "power_up": "Power Up", - "submit_a_story": "Post", - "tag": "Tag", - "to": "a", - "all_tags": "All tags", - "transfer": "Trasferisci", - "trending_topics": "Trending Topics", - "type": "Scrivi", - "unfollow": "Non seguire più", - "unmute": "Unmute", - "unknown": "Sconosciuto", - "upvote": "Upvote", - "upvote_post": "Upvote post", - "username": "Username", - "version": "Versione", - "vote": "Vota", - "votes": "voti", - "wallet": "Wallet", - "warning": "avviso", - "yes": "Sì", - "posting": "Postare", - "owner": "Proprietario", - "active": "Attivo", - "account_not_found": "Account non trovato", - "this_is_wrong_password": "Password errata", - "do_you_need_to": "Hai bisogno di", - "account_name": "Nome Account", - "recover_your_account": "recupera il tuo account", - "reset_usernames_password": "Resetta la password di %(username)s", - "this_will_update_usernames_authtype_key": "Questo aggiornerà la %(authType)s key di %(username)s ", - "passwords_do_not_match": "Le password non corrispondono", - "you_need_private_password_or_key_not_a_public_key": "Hai bisogno di una password privata o di una chiave (non di una chiave pubblica)", - "the_rules_of_APP_NAME": { - "one": "La prima regola di %(APP_NAME)sè: Non perdere la tua password.", - "second": "La seconda regola di %(APP_NAME)s è: Non perdere la tua password.", - "third": "La terza regola di %(APP_NAME)s è: Non possiamo recuperare la tua password.", - "fourth": "La quarta regola è: Se puoi ricordare la password, non è sicura.", - "fifth": "La quinta regola è: Usa solo password generate in modo casuale.", - "sixth": "La sesta regola è: Non dire a nessuno la tua password.", - "seventh": "La settima regola è: Salva sempre la tua password." - }, - "recover_password": "Recupera Account", - "current_password": "Password attuale", - "generated_password": "Password generata", - "backup_password_by_storing_it": "Effettua il backup copiando la password in un file di testo o in un gestore di password. ", - "enter_account_show_password": "Inserisci un nome account valido per visualizzare la password", - "click_to_generate_password": "Clicca per generare una password", - "re_enter_generate_password": "Inserisci di nuovo la Password Generata", - "understand_that_APP_NAME_cannot_recover_password": "Ho capito che %(APP_NAME)s non può recuperare le password perse", - "i_saved_password": "Ho salvato in modo sicuro la mia password generata automaticamente", - "update_password": "Aggiorna la password", - "confirm_password": "Conferma la password", - "account_updated": "Account aggiornato", - "password_must_be_characters_or_more": "La password deve essere lunga %(amount)s caratteri o più", - "need_password_or_key": "Hai bisogno di una password o di una chiave privata (non una chiave pubblica)", - "login_to_see_memo": "Esegui il login per visualizzare la memo", - "new_password": "Nuova password", - "incorrect_password": "Password errata", - "username_does_not_exist": "Il nome utente non esiste", - "account_name_should_start_with_a_letter": "Il nome dell'account deve iniziare con una lettera.", - "account_name_should_be_shorter": "Il nome dell'account deve essere più corto.", - "account_name_should_be_longer": "Il nome dell'account deve essere più lungo.", - "account_name_should_have_only_letters_digits_or_dashes": "Il nome account deve essere composto solo da lettere, cifre e trattini.", - "cannot_increase_reward_of_post_within_the_last_minute_before_payout": "Non è possibile aumentare le ricompense del post durante l'ultimo minuto prima del pagamento", - "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": "Attualmente il voto esiste, l'utente deve indicare la volonta di rigettare il witness ", - "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": "è possibile utilizzare un solo Steem account per indirizzo IP ogni 10 minuti", - "resteem_this_post": "Fai il resteem di questo post", - "reblog": "Resteem", - "write_your_story": "Scrivi la tua storia", - "remember_voting_and_posting_key": "Ricorda la chiave di voto e di pubblicazione", - "auto_login_question_mark": "Accesso automatico?", - "hide_private_key": "Nascondi la chiave privata", - "show_private_key": "Mostra la chiave privata", - "login_to_show": "Esegui il login per mostrare", - "not_valid_email": "Email non valida", - "thank_you_for_being_an_early_visitor_to_APP_NAME": "Ti ringraziamo per essere uno dei primi visitatori di %(APP_NAME)s. Sarai ricontattato il prima possibile. ", - "author_rewards": "Ricompense da autore", - "curation_rewards": "Ricompense da curatore", - "sorry_your_reddit_account_doesnt_have_enough_karma": "Purtroppo il tuo account Reddit non possiede abbastanza Reddit Karma per essere usato per la registrazione gratuita. Aggiungi per favore la tua email per ottenere un posto in lista d'attesa.", - "register_with_facebook": "Registrati con Facebook", - "or_click_the_button_below_to_register_with_facebook": "O premi il pulsante sottostante per registrarti con Facebook", - "server_returned_error": "Errore del server", - "APP_NAME_support": "Supporto %(APP_NAME)s", - "please_email_questions_to": "Per piacere invia le tue domande tramite email a", - "next_7_strings_single_block": { - "authors_get_paid_when_people_like_you_upvote_their_post": "Gli autori ricevono un compenso quando gli utenti come te votano il loro post. ", - "if_you_enjoyed_what_you_read_earn_amount": "Se ti piace quello che hai letto, crea il tuo account oggi e inizia ricevendo STEEM GRATIS!", - "free_steem": "STEEM GRATIS!", - "sign_up_earn_steem": "Registrati adesso per guadagnare" - }, - "next_3_strings_together": { - "show_more": "Mostra di più", - "show_less": "Mostra di meno", - "value_posts": "post di basso valore" - }, - "read_only_mode": "Server in manutenzione, modalità sola lettura attiva. Ci scusiamo per l'inconveniente.", - "tags_and_topics": "Tag e argomenti", - "show_more_topics": "Vedi più argomenti", - "basic": "Base", - "advanced": "Avanzato", - "views": { - "zero": "Nessuna Visualizzazione", - "one": "%(count)s Visualizzazione", - "other": "%(count)s Visualizzazioni" - }, - "responses": { - "zero": "Nessuna Risposta", - "one": "%(count)s Risposta", - "other": "%(count)s Risposte" - }, - "post_key_warning": { - "confirm": "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", - "warning": "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", - "checkbox": "I understand" - } - }, - "navigation": { - "about": "Circa", - "explore": "Esplora", - "APP_NAME_whitepaper": "%(APP_NAME)s Whitepaper", - "buy_LIQUID_TOKEN": "Compra %(LIQUID_TOKEN)s", - "sell_LIQUID_TOKEN": "Vendi %(LIQUID_TOKEN)s", - "currency_market": "Mercato della valuta", - "stolen_account_recovery": "Recupero degli Account Rubati", - "change_account_password": "Cambia la password dell'account", - "witnesses": "Witness", - "vote_for_witnesses": "Vota per i Witness", - "privacy_policy": "Privacy Policy", - "terms_of_service": "Condizioni di servizio", - "sign_up": "Entra", - "learn_more": "Scopri di più", - "welcome": "Benvenuto", - "faq": "FAQ", - "shop": "The Steemit Shop", - "chat": "Steemit Chat", - "app_center": "Steemit App Center", - "api_docs": "Steemit API Docs", - "bluepaper": "Steem Bluepaper", - "smt_whitepaper": "SMT Whitepaper", - "whitepaper": "Steem Whitepaper", - "intro_tagline": "Il denaro parla", - "intro_paragraph": "La tua voce vale qualcosa. Entra nella comunità che ti pagare per pubblicare e curare contenuti di alta qualità." - }, - "main_menu": { - "hot": "hot", - "trending": "trending" - }, - "reply_editor": { - "shorten_title": "Titolo breve", - "exceeds_maximum_length": "Supera la grandezza massima (%(maxKb)sKB)", - "including_the_category": " (inclusa la categoria '%(rootCategory)s')", - "use_limited_amount_of_tags": "Hai %(tagsLength)s tag in totale %(includingCategory)s. Usane solo 5 nel tuo post e nella linea delle categorie.", - "are_you_sure_you_want_to_clear_this_form": "Sicuro di voler cancellare questo form?", - "uploading": "Caricamento", - "draft_saved": "Bozza salvata", - "editor": "Editor", - "insert_images_by_dragging_dropping": "Per inserire le immagini, trascinale qui", - "pasting_from_the_clipboard": "incollando dagli appunti,", - "selecting_them": "selezionandole. ", - "image_upload": "Carica immagine", - "power_up_100": "Power Up 100%%", - "default_50_50": "Default (50%% / 50%%)", - "decline_payout": "Rinuncia al pagamento", - "check_this_to_auto_upvote_your_post": "Clicca qui per effettuare l'upvote automatico del tuo post", - "markdown_styling_guide": "Guida di stile Markdown", - "or_by": "o da", - "title": "Titolo", - "update_post": "Aggiorna il Post", - "markdown_not_supported": "Il Markdown non è supportato qui" - }, - "category_selector_jsx": { - "tag_your_story": "Tag (massimo 5). Il primo tag è la categoria principale. ", - "select_a_tag": "Seleziona un tag", - "maximum_tag_length_is_24_characters": "La lunghezza massima di un tag è 24 caratteri", - "use_limited_amount_of_categories": "Utilizzare solo %(amount)s categorie", - "use_only_lowercase_letters": "Usa solo caratteri minuscoli", - "use_one_dash": "Usa solo un trattino", - "use_spaces_to_separate_tags": "Usa gli spazi per separare i tag", - "use_only_allowed_characters": "Usa solo lettere minuscole, cifre e un trattino", - "must_start_with_a_letter": "Deve iniziare con una lettera", - "must_end_with_a_letter_or_number": "Deve terminare con una lettera o un numero" - }, - "postfull_jsx": { - "this_post_is_not_available_due_to_a_copyright_claim": "Questo post non è disponibile per violazione del copyright.", - "share_on_facebook": "Condividi su Facebook", - "share_on_twitter": "Condividi su Twitter", - "share_on_linkedin": "Condividi su Linkedin", - "recent_password": "Password recente", - "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": "In 3,5 giorni, converti %(amount)s%(DEBT_TOKEN)s in %(LIQUID_TOKEN)s", - "view_the_full_context": "Vedi il contesto completo", - "view_the_direct_parent": "Visualizza il collegamento diretto", - "you_are_viewing_a_single_comments_thread_from": "Stai visualizzando il commento singolo di un thread di " - }, - "market_jsx": { - "action": "Azione", - "date_created": "Data creazione", - "last_price": "Ultimo prezzo", - "24h_volume": "Volume nelle 24 ore", - "spread": "Spread", - "total": "Totale", - "available": "Disponibile", - "lowest_ask": "Prezzo di vendità più basso", - "highest_bid": "Prezzo di acquisto più alto", - "buy_orders": "Ordini d'acquisto", - "sell_orders": "Ordini di vendita", - "trade_history": "Cronologia delle Transazioni", - "open_orders": "Ordini Aperti", - "sell_amount_for_atleast": "Vendi %(amount_to_sell)s per almeno %(min_to_receive)s (%(effectivePrice)s)", - "buy_atleast_amount_for": "Compra ad almeno %(min_to_receive)s per %(amount_to_sell)s (%(effectivePrice)s)", - "price_warning_above": "Questo prezzo è ben al di sopra del prezzo di mercato corrente di %(marketPrice)s, sei sicuro?", - "price_warning_below": "Questo prezzo è ben al di sotto del prezzo di mercato corrente di %(marketPrice)s, sei sicuro?", - "order_cancel_confirm": "Cancella l'ordine %(order_id)s per %(user)s?", - "order_cancelled": "Ordine %(order_id)s cancellato", - "higher": "Più alto", - "lower": "Più basso", - "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": "Totale %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" - }, - "recoveraccountstep1_jsx": { - "begin_recovery": "Inizia il Ripristino", - "not_valid": "Non valido", - "account_name_is_not_found": "Nome account non trovato", - "unable_to_recover_account_not_change_ownership_recently": "Non possiamo recuperare questo account, non ha cambiato proprietario di recente. ", - "password_not_used_in_last_days": "Questa password non viene usata per questo account da 30 giorni. ", - "request_already_submitted_contact_support": "La tua richiesta è stata già inviata e la stiamo lavorando. Puoi contattare %(SUPPORT_EMAIL)sper verificare lo stato della tua richiesta.", - "recover_account_intro": "Può succedere che ogni tanto la chiave personale di un membro di Steemit possa essere compromessa. La funzione \"Recupera account rubato\" dà 30 giorni al legittimo proprietario dell'account per recuperarlo dal momento in cui il ladro cambia la chiave personale. \"Recupera account rubato\" può essere usato solo su %(APP_URL)s se il proprietario dell'account ha precedentemente inserito %(APP_NAME)s come account fidato e accettato i Termini di Servizio di %(APP_NAME)s.", - "login_with_facebook_or_reddit_media_to_verify_identity": "Effettua il login con Facebook o Reddit per verificare la tua identità ", - "login_with_social_media_to_verify_identity": "Effettua il login con %(provider)s per verificare la tua identità", - "enter_email_toverify_identity": "Abbiamo bisogno di verificare la tua identità. Inserisci di seguito il tuo indirizzo email per iniziare la verifica. ", - "continue_with_email": "Continua con l'email", - "thanks_for_submitting_request_for_account_recovery": "Grazie per aver immesso la tua richiesta per Ripristinare il tuo Account usando l'autenticazione multi fattoriale basata sulla blockchain di %(APP_NAME)s. Ti risponderemo il prima possibile, comunque  considera che potrebbero esserci ritardi nella risposta a causa del grade volume di email. Preparati a verificare la tua identità.", - "recovering_account": "Stiamo recuperando l'account", - "recover_account": "Recupero Account", - "checking_account_owner": "Stiamo controllando il proprietario dell'account", - "sending_recovery_request": "Invia richiesta di recupero ", - "cant_confirm_account_ownership": "Non siamo riusciti a confermare la proprietà dell'account. Controlla la password.", - "account_recovery_request_not_confirmed": "La richiesta di recupero account non è ancora stata confermata. Per favore, ritorna tra un po'. Grazie per la pazienza! " - }, - "user_profile": { - "unknown_account": "Account sconosciuto", - "user_hasnt_made_any_posts_yet": "Sembra che %(name)snon abbia ancora pubblicato nessun post! ", - "user_hasnt_started_bloggin_yet": "Sembra che %(name)s non abbia ancora iniziato a scrivere!", - "user_hasnt_followed_anything_yet": "Sembra che %(name)snon stia seguendo ancora nessuno! Se %(name)sha aggiunto di recente nuovi utenti da seguire, il feed personalizzato si riempirà di contenuti non appena ce ne saranno di nuovi. ", - "user_hasnt_had_any_replies_yet": "%(name)s hasn't had any replies yet", - "looks_like_you_havent_posted_anything_yet": "Looks like you haven't posted anything yet.", - "create_a_post": "Create a Post", - "explore_trending_articles": "Explore Trending Articles", - "read_the_quick_start_guide": "Read The Quick Start Guide", - "browse_the_faq": "Browse The FAQ", - "followers": "Followers", - "this_is_users_reputations_score_it_is_based_on_history_of_votes": "Questo è il livello di reputazione di %(name)s. \n\nIl livello di reputazione si basa sulla storia dei voti ricevuti dall'account e viene utilizzato per nascondere contenuti di scarsa qualità. ", - "follower_count": { - "zero": "Nessun follower", - "one": "1 follower", - "other": "%(count)s follower" - }, - "followed_count": { - "zero": "Non segue nessuno", - "one": "1 following", - "other": "%(count)s following" - }, - "post_count": { - "zero": "Nessun post", - "one": "1 post", - "other": "%(count)s post" - } - }, - "authorrewards_jsx": { - "estimated_author_rewards_last_week": "Ricompense per l'autore stimate durante l'ultima settimana", - "author_rewards_history": "Cronologia di Ricompense dell'autore" - }, - "curationrewards_jsx": { - "estimated_curation_rewards_last_week": "Ricompense da curatore stimate nell'ultima settimana", - "curation_rewards_history": "Cronologia ricompense da curatore" - }, - "post_jsx": { - "now_showing_comments_with_low_ratings": "Commenti con voti bassi rivelati", - "sort_order": "Ordinamento", - "comments_were_hidden_due_to_low_ratings": "Commenti nascosti a causa del basso rating" - }, - "voting_jsx": { - "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": "Flaggare un post può rimuovere le ricompense e rendere questo materiale meno visibile. Alcune ragioni comuni per effettuare un flag sono", - "disagreement_on_rewards": "Disaccordo sulle ricompense", - "fraud_or_plagiarism": "Frode o Plagio", - "hate_speech_or_internet_trolling": "Incitamento all'odio o Trolling", - "intentional_miss_categorized_content_or_spam": "Contenuto volutamente categorizzato impropriamente o spam", - "pending_payout": "Pagamento in attesa $%(value)s", - "payout_declined": "Pagamento Rifiutato", - "max_accepted_payout": "Il pagamento massimo accettato è di %(value)s $", - "promotion_cost": "Costo della Promozione $%(value)s", - "past_payouts": "Pagamenti precedenti di %(value)s $", - "past_payouts_author": "- Autore %(value)s $", - "past_payouts_curators": "- Curatori %(value)s $", - "we_will_reset_curation_rewards_for_this_post": "sarà effettuato il reset delle ricompense del curatore in questo post. ", - "removing_your_vote": "Rimuovi il tuo voto", - "changing_to_an_upvote": "Cambia in un upvote", - "changing_to_a_downvote": "Cambia in un downvote", - "confirm_flag": "Conferma il flag", - "and_more": "e %(count)s in più", - "votes_plural": { - "one": "%(count)s voto", - "other": "%(count)s voti" - } - }, - "witnesses_jsx": { - "witness_thread": "witness thread", - "top_witnesses": "Voto Witness ", - "you_have_votes_remaining": { - "zero": "Non hai voti residui", - "one": "Ti resta 1 voto", - "other": "Hai %(count)svoti residui" - }, - "you_can_vote_for_maximum_of_witnesses": "Puoi votare un massimo di 30 witness", - "witness": "Witness", - "information": "Informazioni", - "if_you_want_to_vote_outside_of_top_enter_account_name": "Se desideri votare un witness al di fuori della top 50, inserisci di seguito il nome account per effettuare il voto. ", - "set_witness_proxy": "Puoi anche scegliere un proxy che voterà i witnesses per te. Questo resetterà la tua attuale selezione di testimoni.", - "witness_set": "Hai settato un proxy di voto. Se volessi riabilitare il voto manuale, cancella il tuo proxy.", - "witness_proxy_current": "Il tuo attuale proxy è:", - "witness_proxy_set": "Setta il proxy", - "witness_proxy_clear": "Cancella il proxy", - "proxy_update_error": "Il tuo proxy non è aggiornato" - }, - "votesandcomments_jsx": { - "no_responses_yet_click_to_respond": "Non c'è ancora nessuna risposta. Clicca per rispondere.", - "response_count_tooltip": { - "zero": "Nessun commento. Clicca per commentare.", - "one": "1 commento. Clicca per commentare.", - "other": "%(count)s commenti. Clicca per commentare." - }, - "vote_count": { - "zero": "Nessun voto", - "one": "1 voto", - "other": "%(count)s voti" + "g": { + "age": "età", + "amount": "Quantità", + "and": "e", + "are_you_sure": "Sei sicuro?", + "ask": "Ask", + "balance": "Saldo", + "balances": "Saldi", + "bid": "Bid", + "blog": "Blog", + "browse": "Naviga", + "buy": "Acquista", + "buy_or_sell": "Acquista o vendi", + "by": "da", + "cancel": "Cancella", + "change_password": "Cambia Password", + "choose_language": "Scegli la lingua", + "clear": "Pulisci", + "close": "Chiudi", + "collapse_or_expand": "Riduci/Espandi", + "comments": "Commenti", + "confirm": "Conferma", + "convert": "Converti", + "date": "Data", + "delete": "Elimina", + "dismiss": "Abbandona", + "edit": "Modifica", + "email": "Email", + "feed": "Feed", + "follow": "Segui", + "for": "per", + "from": "da", + "go_back": "Indietro", + "hide": "Nascondi", + "in": "in", + "in_reply_to": "in risposta a", + "insufficient_balance": "Credito insufficiente", + "invalid_amount": "Importo errato", + "joined": "Connesso", + "loading": "Caricamento", + "login": "Login", + "logout": "Logout", + "memo": "Memo", + "mute": "Silenzia", + "new": "Nuovo", + "newer": "Più recente", + "next": "Prossimo", + "no": "No", + "ok": "Ok", + "older": "Meno recente", + "or": "o", + "order_placed": "Ordine piazzato", + "password": "Password", + "payouts": "Pagamenti", + "permissions": "Permessi", + "phishy_message": + "Link expanded to plain text; beware of a potential phishing attempt", + "post": "Post", + "post_as": "Pubblica come", + "posts": "Posts", + "powered_up_100": "Powered Up 100%%", + "preview": "Anteprima", + "previous": "Precedente", + "price": "Prezzo", + "print": "Stampa", + "promote": "Promuovi", + "promoted": "promosso", + "re": "RE", + "re_to": "RE: %(topic)s", + "recent_password": "Password Recente", + "receive": "Ricevi", + "remove": "Rimuovi", + "remove_vote": "Rimuovi il voto", + "replied_to": "risposto a %(account)s", + "replies": "Risponde", + "reply": "Risposta", + "reply_count": { + "zero": "Nessuna risposta", + "one": "1 risposta", + "other": "%(count)s risposte" + }, + "reputation": "Reputazione", + "reveal_comment": "Mostra Commento", + "request": "richiesta", + "required": "Richiesto", + "rewards": "Ricompense", + "save": "Salva", + "saved": "Salvato", + "search": "Cerca", + "sell": "Vendi", + "settings": "Impostazioni", + "share_this_post": "Condividi questo post", + "show": "Mostra", + "sign_in": "Accedi", + "sign_up": "Registrati", + "since": "da", + "submit": "Sottoscrivi", + "power_up": "Power Up", + "submit_a_story": "Post", + "tag": "Tag", + "to": "a", + "all_tags": "All tags", + "transfer": "Trasferisci", + "trending_topics": "Trending Topics", + "type": "Scrivi", + "unfollow": "Non seguire più", + "unmute": "Unmute", + "unknown": "Sconosciuto", + "upvote": "Upvote", + "upvote_post": "Upvote post", + "username": "Username", + "version": "Versione", + "vote": "Vota", + "votes": "voti", + "wallet": "Wallet", + "warning": "avviso", + "yes": "Sì", + "posting": "Postare", + "owner": "Proprietario", + "active": "Attivo", + "account_not_found": "Account non trovato", + "this_is_wrong_password": "Password errata", + "do_you_need_to": "Hai bisogno di", + "account_name": "Nome Account", + "recover_your_account": "recupera il tuo account", + "reset_usernames_password": "Resetta la password di %(username)s", + "this_will_update_usernames_authtype_key": + "Questo aggiornerà la %(authType)s key di %(username)s ", + "passwords_do_not_match": "Le password non corrispondono", + "you_need_private_password_or_key_not_a_public_key": + "Hai bisogno di una password privata o di una chiave (non di una chiave pubblica)", + "the_rules_of_APP_NAME": { + "one": + "La prima regola di %(APP_NAME)sè: Non perdere la tua password.", + "second": + "La seconda regola di %(APP_NAME)s è: Non perdere la tua password.", + "third": + "La terza regola di %(APP_NAME)s è: Non possiamo recuperare la tua password.", + "fourth": + "La quarta regola è: Se puoi ricordare la password, non è sicura.", + "fifth": + "La quinta regola è: Usa solo password generate in modo casuale.", + "sixth": "La sesta regola è: Non dire a nessuno la tua password.", + "seventh": "La settima regola è: Salva sempre la tua password." + }, + "recover_password": "Recupera Account", + "current_password": "Password attuale", + "generated_password": "Password generata", + "backup_password_by_storing_it": + "Effettua il backup copiando la password in un file di testo o in un gestore di password. ", + "enter_account_show_password": + "Inserisci un nome account valido per visualizzare la password", + "click_to_generate_password": "Clicca per generare una password", + "re_enter_generate_password": "Inserisci di nuovo la Password Generata", + "understand_that_APP_NAME_cannot_recover_password": + "Ho capito che %(APP_NAME)s non può recuperare le password perse", + "i_saved_password": + "Ho salvato in modo sicuro la mia password generata automaticamente", + "update_password": "Aggiorna la password", + "confirm_password": "Conferma la password", + "account_updated": "Account aggiornato", + "password_must_be_characters_or_more": + "La password deve essere lunga %(amount)s caratteri o più", + "need_password_or_key": + "Hai bisogno di una password o di una chiave privata (non una chiave pubblica)", + "login_to_see_memo": "Esegui il login per visualizzare la memo", + "new_password": "Nuova password", + "incorrect_password": "Password errata", + "username_does_not_exist": "Il nome utente non esiste", + "account_name_should_start_with_a_letter": + "Il nome dell'account deve iniziare con una lettera.", + "account_name_should_be_shorter": + "Il nome dell'account deve essere più corto.", + "account_name_should_be_longer": + "Il nome dell'account deve essere più lungo.", + "account_name_should_have_only_letters_digits_or_dashes": + "Il nome account deve essere composto solo da lettere, cifre e trattini.", + "cannot_increase_reward_of_post_within_the_last_minute_before_payout": + "Non è possibile aumentare le ricompense del post durante l'ultimo minuto prima del pagamento", + "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": + "Attualmente il voto esiste, l'utente deve indicare la volonta di rigettare il witness ", + "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": + "è possibile utilizzare un solo Steem account per indirizzo IP ogni 10 minuti", + "resteem_this_post": "Fai il resteem di questo post", + "reblog": "Resteem", + "write_your_story": "Scrivi la tua storia", + "remember_voting_and_posting_key": + "Ricorda la chiave di voto e di pubblicazione", + "auto_login_question_mark": "Accesso automatico?", + "hide_private_key": "Nascondi la chiave privata", + "show_private_key": "Mostra la chiave privata", + "login_to_show": "Esegui il login per mostrare", + "not_valid_email": "Email non valida", + "thank_you_for_being_an_early_visitor_to_APP_NAME": + "Ti ringraziamo per essere uno dei primi visitatori di %(APP_NAME)s. Sarai ricontattato il prima possibile. ", + "author_rewards": "Ricompense da autore", + "curation_rewards": "Ricompense da curatore", + "sorry_your_reddit_account_doesnt_have_enough_karma": + "Purtroppo il tuo account Reddit non possiede abbastanza Reddit Karma per essere usato per la registrazione gratuita. Aggiungi per favore la tua email per ottenere un posto in lista d'attesa.", + "register_with_facebook": "Registrati con Facebook", + "or_click_the_button_below_to_register_with_facebook": + "O premi il pulsante sottostante per registrarti con Facebook", + "server_returned_error": "Errore del server", + "APP_NAME_support": "Supporto %(APP_NAME)s", + "please_email_questions_to": + "Per piacere invia le tue domande tramite email a", + "next_7_strings_single_block": { + "authors_get_paid_when_people_like_you_upvote_their_post": + "Gli autori ricevono un compenso quando gli utenti come te votano il loro post. ", + "if_you_enjoyed_what_you_read_earn_amount": + "Se ti piace quello che hai letto, crea il tuo account oggi e inizia ricevendo STEEM GRATIS!", + "free_steem": "STEEM GRATIS!", + "sign_up_earn_steem": "Registrati adesso per guadagnare" + }, + "next_3_strings_together": { + "show_more": "Mostra di più", + "show_less": "Mostra di meno", + "value_posts": "post di basso valore" + }, + "read_only_mode": + "Server in manutenzione, modalità sola lettura attiva. Ci scusiamo per l'inconveniente.", + "tags_and_topics": "Tag e argomenti", + "show_more_topics": "Vedi più argomenti", + "basic": "Base", + "advanced": "Avanzato", + "views": { + "zero": "Nessuna Visualizzazione", + "one": "%(count)s Visualizzazione", + "other": "%(count)s Visualizzazioni" + }, + "responses": { + "zero": "Nessuna Risposta", + "one": "%(count)s Risposta", + "other": "%(count)s Risposte" + }, + "post_key_warning": { + "confirm": + "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", + "warning": + "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", + "checkbox": "I understand" + } + }, + "navigation": { + "about": "Circa", + "explore": "Esplora", + "APP_NAME_whitepaper": "%(APP_NAME)s Whitepaper", + "buy_LIQUID_TOKEN": "Compra %(LIQUID_TOKEN)s", + "sell_LIQUID_TOKEN": "Vendi %(LIQUID_TOKEN)s", + "currency_market": "Mercato della valuta", + "stolen_account_recovery": "Recupero degli Account Rubati", + "change_account_password": "Cambia la password dell'account", + "witnesses": "Witness", + "vote_for_witnesses": "Vota per i Witness", + "privacy_policy": "Privacy Policy", + "terms_of_service": "Condizioni di servizio", + "sign_up": "Entra", + "learn_more": "Scopri di più", + "welcome": "Benvenuto", + "faq": "FAQ", + "shop": "The Steemit Shop", + "chat": "Steemit Chat", + "app_center": "Steemit App Center", + "api_docs": "Steemit API Docs", + "bluepaper": "Steem Bluepaper", + "smt_whitepaper": "SMT Whitepaper", + "whitepaper": "Steem Whitepaper", + "intro_tagline": "Il denaro parla", + "intro_paragraph": + "La tua voce vale qualcosa. Entra nella comunità che ti pagare per pubblicare e curare contenuti di alta qualità." + }, + "main_menu": { + "hot": "hot", + "trending": "trending" + }, + "reply_editor": { + "shorten_title": "Titolo breve", + "exceeds_maximum_length": "Supera la grandezza massima (%(maxKb)sKB)", + "including_the_category": " (inclusa la categoria '%(rootCategory)s')", + "use_limited_amount_of_tags": + "Hai %(tagsLength)s tag in totale %(includingCategory)s. Usane solo 5 nel tuo post e nella linea delle categorie.", + "are_you_sure_you_want_to_clear_this_form": + "Sicuro di voler cancellare questo form?", + "uploading": "Caricamento", + "draft_saved": "Bozza salvata", + "editor": "Editor", + "insert_images_by_dragging_dropping": + "Per inserire le immagini, trascinale qui", + "pasting_from_the_clipboard": "incollando dagli appunti,", + "selecting_them": "selezionandole. ", + "image_upload": "Carica immagine", + "power_up_100": "Power Up 100%%", + "default_50_50": "Default (50%% / 50%%)", + "decline_payout": "Rinuncia al pagamento", + "check_this_to_auto_upvote_your_post": + "Clicca qui per effettuare l'upvote automatico del tuo post", + "markdown_styling_guide": "Guida di stile Markdown", + "or_by": "o da", + "title": "Titolo", + "update_post": "Aggiorna il Post", + "markdown_not_supported": "Il Markdown non è supportato qui" + }, + "category_selector_jsx": { + "tag_your_story": + "Tag (massimo 5). Il primo tag è la categoria principale. ", + "select_a_tag": "Seleziona un tag", + "maximum_tag_length_is_24_characters": + "La lunghezza massima di un tag è 24 caratteri", + "use_limited_amount_of_categories": + "Utilizzare solo %(amount)s categorie", + "use_only_lowercase_letters": "Usa solo caratteri minuscoli", + "use_one_dash": "Usa solo un trattino", + "use_spaces_to_separate_tags": "Usa gli spazi per separare i tag", + "use_only_allowed_characters": + "Usa solo lettere minuscole, cifre e un trattino", + "must_start_with_a_letter": "Deve iniziare con una lettera", + "must_end_with_a_letter_or_number": + "Deve terminare con una lettera o un numero" + }, + "postfull_jsx": { + "this_post_is_not_available_due_to_a_copyright_claim": + "Questo post non è disponibile per violazione del copyright.", + "share_on_facebook": "Condividi su Facebook", + "share_on_twitter": "Condividi su Twitter", + "share_on_linkedin": "Condividi su Linkedin", + "recent_password": "Password recente", + "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": + "In 3,5 giorni, converti %(amount)s%(DEBT_TOKEN)s in %(LIQUID_TOKEN)s", + "view_the_full_context": "Vedi il contesto completo", + "view_the_direct_parent": "Visualizza il collegamento diretto", + "you_are_viewing_a_single_comments_thread_from": + "Stai visualizzando il commento singolo di un thread di " + }, + "market_jsx": { + "action": "Azione", + "date_created": "Data creazione", + "last_price": "Ultimo prezzo", + "24h_volume": "Volume nelle 24 ore", + "spread": "Spread", + "total": "Totale", + "available": "Disponibile", + "lowest_ask": "Prezzo di vendità più basso", + "highest_bid": "Prezzo di acquisto più alto", + "buy_orders": "Ordini d'acquisto", + "sell_orders": "Ordini di vendita", + "trade_history": "Cronologia delle Transazioni", + "open_orders": "Ordini Aperti", + "sell_amount_for_atleast": + "Vendi %(amount_to_sell)s per almeno %(min_to_receive)s (%(effectivePrice)s)", + "buy_atleast_amount_for": + "Compra ad almeno %(min_to_receive)s per %(amount_to_sell)s (%(effectivePrice)s)", + "price_warning_above": + "Questo prezzo è ben al di sopra del prezzo di mercato corrente di %(marketPrice)s, sei sicuro?", + "price_warning_below": + "Questo prezzo è ben al di sotto del prezzo di mercato corrente di %(marketPrice)s, sei sicuro?", + "order_cancel_confirm": "Cancella l'ordine %(order_id)s per %(user)s?", + "order_cancelled": "Ordine %(order_id)s cancellato", + "higher": "Più alto", + "lower": "Più basso", + "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": + "Totale %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" + }, + "recoveraccountstep1_jsx": { + "begin_recovery": "Inizia il Ripristino", + "not_valid": "Non valido", + "account_name_is_not_found": "Nome account non trovato", + "unable_to_recover_account_not_change_ownership_recently": + "Non possiamo recuperare questo account, non ha cambiato proprietario di recente. ", + "password_not_used_in_last_days": + "Questa password non viene usata per questo account da 30 giorni. ", + "request_already_submitted_contact_support": + "La tua richiesta è stata già inviata e la stiamo lavorando. Puoi contattare %(SUPPORT_EMAIL)sper verificare lo stato della tua richiesta.", + "recover_account_intro": + "Può succedere che ogni tanto la chiave personale di un membro di Steemit possa essere compromessa. La funzione \"Recupera account rubato\" dà 30 giorni al legittimo proprietario dell'account per recuperarlo dal momento in cui il ladro cambia la chiave personale. \"Recupera account rubato\" può essere usato solo su %(APP_URL)s se il proprietario dell'account ha precedentemente inserito %(APP_NAME)s come account fidato e accettato i Termini di Servizio di %(APP_NAME)s.", + "login_with_facebook_or_reddit_media_to_verify_identity": + "Effettua il login con Facebook o Reddit per verificare la tua identità ", + "login_with_social_media_to_verify_identity": + "Effettua il login con %(provider)s per verificare la tua identità", + "enter_email_toverify_identity": + "Abbiamo bisogno di verificare la tua identità. Inserisci di seguito il tuo indirizzo email per iniziare la verifica. ", + "continue_with_email": "Continua con l'email", + "thanks_for_submitting_request_for_account_recovery": + "Grazie per aver immesso la tua richiesta per Ripristinare il tuo Account usando l'autenticazione multi fattoriale basata sulla blockchain di %(APP_NAME)s. Ti risponderemo il prima possibile, comunque  considera che potrebbero esserci ritardi nella risposta a causa del grade volume di email. Preparati a verificare la tua identità.", + "recovering_account": "Stiamo recuperando l'account", + "recover_account": "Recupero Account", + "checking_account_owner": + "Stiamo controllando il proprietario dell'account", + "sending_recovery_request": "Invia richiesta di recupero ", + "cant_confirm_account_ownership": + "Non siamo riusciti a confermare la proprietà dell'account. Controlla la password.", + "account_recovery_request_not_confirmed": + "La richiesta di recupero account non è ancora stata confermata. Per favore, ritorna tra un po'. Grazie per la pazienza! " + }, + "user_profile": { + "unknown_account": "Account sconosciuto", + "user_hasnt_made_any_posts_yet": + "Sembra che %(name)snon abbia ancora pubblicato nessun post! ", + "user_hasnt_started_bloggin_yet": + "Sembra che %(name)s non abbia ancora iniziato a scrivere!", + "user_hasnt_followed_anything_yet": + "Sembra che %(name)snon stia seguendo ancora nessuno! Se %(name)sha aggiunto di recente nuovi utenti da seguire, il feed personalizzato si riempirà di contenuti non appena ce ne saranno di nuovi. ", + "user_hasnt_had_any_replies_yet": "%(name)s hasn't had any replies yet", + "looks_like_you_havent_posted_anything_yet": + "Looks like you haven't posted anything yet.", + "create_a_post": "Create a Post", + "explore_trending_articles": "Explore Trending Articles", + "read_the_quick_start_guide": "Read The Quick Start Guide", + "browse_the_faq": "Browse The FAQ", + "followers": "Followers", + "this_is_users_reputations_score_it_is_based_on_history_of_votes": + "Questo è il livello di reputazione di %(name)s. \n\nIl livello di reputazione si basa sulla storia dei voti ricevuti dall'account e viene utilizzato per nascondere contenuti di scarsa qualità. ", + "follower_count": { + "zero": "Nessun follower", + "one": "1 follower", + "other": "%(count)s follower" + }, + "followed_count": { + "zero": "Non segue nessuno", + "one": "1 following", + "other": "%(count)s following" + }, + "post_count": { + "zero": "Nessun post", + "one": "1 post", + "other": "%(count)s post" + } + }, + "authorrewards_jsx": { + "estimated_author_rewards_last_week": + "Ricompense per l'autore stimate durante l'ultima settimana", + "author_rewards_history": "Cronologia di Ricompense dell'autore" + }, + "curationrewards_jsx": { + "estimated_curation_rewards_last_week": + "Ricompense da curatore stimate nell'ultima settimana", + "curation_rewards_history": "Cronologia ricompense da curatore" + }, + "post_jsx": { + "now_showing_comments_with_low_ratings": + "Commenti con voti bassi rivelati", + "sort_order": "Ordinamento", + "comments_were_hidden_due_to_low_ratings": + "Commenti nascosti a causa del basso rating" + }, + "voting_jsx": { + "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": + "Flaggare un post può rimuovere le ricompense e rendere questo materiale meno visibile. Alcune ragioni comuni per effettuare un flag sono", + "disagreement_on_rewards": "Disaccordo sulle ricompense", + "fraud_or_plagiarism": "Frode o Plagio", + "hate_speech_or_internet_trolling": "Incitamento all'odio o Trolling", + "intentional_miss_categorized_content_or_spam": + "Contenuto volutamente categorizzato impropriamente o spam", + "pending_payout": "Pagamento in attesa $%(value)s", + "payout_declined": "Pagamento Rifiutato", + "max_accepted_payout": + "Il pagamento massimo accettato è di %(value)s $", + "promotion_cost": "Costo della Promozione $%(value)s", + "past_payouts": "Pagamenti precedenti di %(value)s $", + "past_payouts_author": "- Autore %(value)s $", + "past_payouts_curators": "- Curatori %(value)s $", + "we_will_reset_curation_rewards_for_this_post": + "sarà effettuato il reset delle ricompense del curatore in questo post. ", + "removing_your_vote": "Rimuovi il tuo voto", + "changing_to_an_upvote": "Cambia in un upvote", + "changing_to_a_downvote": "Cambia in un downvote", + "confirm_flag": "Conferma il flag", + "and_more": "e %(count)s in più", + "votes_plural": { + "one": "%(count)s voto", + "other": "%(count)s voti" + } + }, + "witnesses_jsx": { + "witness_thread": "witness thread", + "top_witnesses": "Voto Witness ", + "you_have_votes_remaining": { + "zero": "Non hai voti residui", + "one": "Ti resta 1 voto", + "other": "Hai %(count)svoti residui" + }, + "you_can_vote_for_maximum_of_witnesses": + "Puoi votare un massimo di 30 witness", + "witness": "Witness", + "information": "Informazioni", + "if_you_want_to_vote_outside_of_top_enter_account_name": + "Se desideri votare un witness al di fuori della top 50, inserisci di seguito il nome account per effettuare il voto. ", + "set_witness_proxy": + "Puoi anche scegliere un proxy che voterà i witnesses per te. Questo resetterà la tua attuale selezione di testimoni.", + "witness_set": + "Hai settato un proxy di voto. Se volessi riabilitare il voto manuale, cancella il tuo proxy.", + "witness_proxy_current": "Il tuo attuale proxy è:", + "witness_proxy_set": "Setta il proxy", + "witness_proxy_clear": "Cancella il proxy", + "proxy_update_error": "Il tuo proxy non è aggiornato" + }, + "votesandcomments_jsx": { + "no_responses_yet_click_to_respond": + "Non c'è ancora nessuna risposta. Clicca per rispondere.", + "response_count_tooltip": { + "zero": "Nessun commento. Clicca per commentare.", + "one": "1 commento. Clicca per commentare.", + "other": "%(count)s commenti. Clicca per commentare." + }, + "vote_count": { + "zero": "Nessun voto", + "one": "1 voto", + "other": "%(count)s voti" + } + }, + "userkeys_jsx": { + "public": "Pubblico", + "private": "Privato", + "public_something_key": "%(key)sChiave Pubblica", + "private_something_key": "%(key)sChiave Privata", + "posting_key_is_required_it_should_be_different": + "La chiave di pubblicazione si usa per pubblicare e votare. Dovrebbe essere diversa dalla chiave attiva e dalla chiave proprietaria. ", + "the_active_key_is_used_to_make_transfers_and_place_orders": + "La chiave attiva si usa per effettuare trasferimenti e per piazzare ordini nel market interno. ", + "the_owner_key_is_required_to_change_other_keys": + "La chiave proprietaria è la chiave principale dell'account e serve a cambiare le altre chiavi. ", + "the_private_key_or_password_should_be_kept_offline": + "Raccomandiamo di conservare offline la chiave privata e la password della chiave proprietaria. ", + "the_memo_key_is_used_to_create_and_read_memos": + "La chiave memo si usa per creare e leggere i memo. " + }, + "suggestpassword_jsx": { + "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": + "%(APP_NAME)snon può recuperare la password. Conserva questa pagina in un luogo sicuro, come ad esempio una cassaforte o una cassetta di sicurezza. ", + "APP_NAME_password_backup": "Password Backup di %(APP_NAME)s", + "APP_NAME_password_backup_required": + "Password Backup di %(APP_NAME)s (richiesto)", + "after_printing_write_down_your_user_name": + "Dopo la stampa, scrivi il tuo nome utente. " + }, + "converttosteem_jsx": { + "your_existing_DEBT_TOKEN_are_liquid_and_transferable": + "I tuoi %(DEBT_TOKEN)s sono liquidi e trasferibili. Invece se desiderassi scambiare %(DEBT_TOKEN)s direttamente in questo sito su %(link)s o trasferirli su un mercato esterno.", + "this_is_a_price_feed_conversion": + "Questa è una conversione di prezzo. L'attesa di 3,5 giorni è necessaria per evitare di abusare della media dei prezzi.", + "convert_to_LIQUID_TOKEN": "Converti in %(LIQUID_TOKEN)s", + "DEBT_TOKEN_will_be_unavailable": + "Questa azione avverrà tra 3,5 giorni da adesso e non può essere cancellata. Questi%(DEBT_TOKEN)s diverranno subito indisponibili" + }, + "tips_js": { + "liquid_token": + "Token scambiabili che possono essere trasferiti ovunque in qualsiasi momento.
        %(LIQUID_TOKEN)s possono essere convertiti in %(VESTING_TOKEN)s con un processo chiamato powering up.", + "influence_token": + "Token che ti danno più controllo sui pagamenti dei post e ti consentono di guadagnare sulle ricompense da curatore.", + "estimated_value": + "Il valore stimato si basa sul valore medio di %(LIQUID_TOKEN)s in dollari US. ", + "non_transferable": + "%(VESTING_TOKEN)s non è trasferibile e richiede 3 mesi, (13 pagamenti) per riconvertirli in %(LIQUID_TOKEN)s.", + "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": + "I %(VESTING_TOKEN)s convertiti possono essere spediti a te stesso o a qualcun altro ma non possono non ritrasferirli senza essere convertirli di nuovo in %(LIQUID_TOKEN)s.", + "part_of_your_steem_power_is_currently_delegated": + "Parte del tuo STEEM POWER ti è attualmente delegato. La delegazione è donata per l'influenza o per aiutare i nuovi utenti ad eseguire le azioni su steemit. Il quantitativo delegato può fluttuare." + }, + "promote_post_jsx": { + "promote_post": "Promuovi post", + "spend_your_DEBT_TOKEN_to_advertise_this_post": + "Spendi %(DEBT_TOKEN)s per pubblicizzare questo post nella sezione dei contenuti promossi.", + "you_successfully_promoted_this_post": + "Hai promosso questo post con successo", + "this_post_was_hidden_due_to_low_ratings": + "Questo post è stato nascosto a causa della bassa reputazione" + }, + "about_jsx": { + "about_app": "About %(APP_NAME)s", + "about_app_details": + "%(APP_NAME)s è una piattaforma di social media dove tutti vengono ricompensati per la creazione e la cura di contenuti. Utilizza un complesso sistema a punti digitali, chiamato Steem che possiede un valore effettivo per le ricompense digitali attraverso la scoperta dei prezzi di mercato e la liquidità. ", + "learn_more_at_app_url": "Scopri di più %(APP_URL)s", + "resources": "Risorse" + }, + "markdownviewer_jsx": { + "images_were_hidden_due_to_low_ratings": + "Le immagini sono state nascoste a causa della bassa reputazione." + }, + "postsummary_jsx": { + "resteemed": "condiviso", + "resteemed_by": "Condiviso da", + "reveal_it": "mostra", + "adjust_your": "sistema il tuo", + "display_preferences": "mostrare le tue preferenze", + "create_an_account": "creare un accout", + "to_save_your_preferences": "per salvare le tue preferenze" + }, + "posts_index": { + "empty_feed_1": "Sembra che tu non stia ancora seguendo nessuno. ", + "empty_feed_2": + "Se hai da poco aggiunto nuovi utenti da seguire, quando ci saranno nuovi contenuti disponibili li vedrai nel tuo feed personalizzato.", + "empty_feed_3": "Esplora gli Articoli in Trending", + "empty_feed_4": "Leggi la Guida di Avvio Rapido", + "empty_feed_5": "Leggi le FAQ" + }, + "transferhistoryrow_jsx": { + "to_savings": "ai risparmi", + "from_savings": "dai risparmi", + "cancel_transfer_from_savings": + "Cancella il trasferimento dai risparmi", + "stop_power_down": "Interrompi il Power Down", + "start_power_down_of": "Avvia il Power Down di", + "receive_interest_of": "Ricevi l'interesse di" + }, + "savingswithdrawhistory_jsx": { + "cancel_this_withdraw_request": + "Cancellare questa richiesta di prelevamento?", + "pending_savings_withdrawals": "PRELIEVI DEI RISPARMI IN SOSPESO", + "withdraw": "Ritira %(amount)s", + "to": "a %(to)s", + "from_to": "da %(from)s a %(to)s" + }, + "explorepost_jsx": { + "copied": "Copiato!", + "copy": "Copia", + "alternative_sources": "Fonti alternative" + }, + "header_jsx": { + "home": "Home", + "create_a_post": "Crea un post", + "change_account_password": "Cambia la password dell'account", + "create_account": "Crea un account", + "stolen_account_recovery": "Recupero account rubato", + "people_following": "Persone che seguono", + "people_followed_by": "Persone seguite da ", + "curation_rewards_by": "Ricompense da curatore da", + "author_rewards_by": "Ricompense da autore da", + "replies_to": "Risposte a ", + "comments_by": "Commenti di" + }, + "loginform_jsx": { + "you_need_a_private_password_or_key": + "Hai bisogno di una password privata o di una chiave (non di una chiave pubblica)", + "cryptography_test_failed": "Test di crittofrafia fallito", + "unable_to_log_you_in": + "Non è possibile effettuare il log in con questo browser.", + "the_latest_versions_of": "Le ultime versioni di ", + "are_well_tested_and_known_to_work_with": + "sono testate e conosciute per funzionare con %(APP_URL)s.", + "due_to_server_maintenance": + "A causa della manutenzione del server siamo in modalità sola lettura. Ci dispiace per l'inconveniente.", + "login_to_vote": "Effettua il login per votare ", + "login_to_post": "Effettua il login per postare", + "login_to_comment": "Effettua il login per commentare", + "posting": "Post", + "active_or_owner": "Attivo o proprietario", + "this_password_is_bound_to_your_account_owner_key": + "Questa password è collegata alla chiave proprietaria del tuo account e non può essere usata per effettuare il login su questo sito. ", + "however_you_can_use_it_to": "Nonostante questo, puoi usarla per", + "update_your_password": "aggiornare la tua password", + "to_obtain_a_more_secure_set_of_keys": + "per ottenere un set di chiavi più sicuro. ", + "this_password_is_bound_to_your_account_active_key": + "Questa password è collegata alla chiave attiva del tuo account e non può essere usata per effettuare il login su questa pagina. ", + "you_may_use_this_active_key_on_other_more": + "Puoi usare la chiave attiva su altre pagine di sicurezza come le pagine Wallet o Market. ", + "you_account_has_been_successfully_created": + "Il tuo account è stato creato con successo! ", + "you_account_has_been_successfully_recovered": + "Il tuo account è stato ripristinato con successo!", + "password_update_succes": + "La password dell'account %(accountName)s è stata aggiornata con successo", + "password_info": + "La password o la chiave privata è stata inserita erroneamente. C'è probabilmente un errore di battitura o un inserimento di dati errato. Suggerimento: Una password o una chiave privata generata da Steemit non conterrà mai i caratteri 0 (zero), O (o maiuscola), I (i maiuscola) e l (l minuscola).", + "enter_your_username": "Inserisci il tuo username", + "password_or_wif": "Password o WIF", + "this_operation_requires_your_key_or_master_password": + "Per eseguire l'operazione è necessaria la propria chiave %(authType)so Master password. ", + "keep_me_logged_in": "Voglio restare connesso", + "amazing_community": "una comunità incredibile", + "to_comment_and_reward_others": "per commentare e premiare gli altri. ", + "sign_up_get_steem": "Sign up. Get STEEM", + "signup_button": "Sign up now to earn ", + "signup_button_emphasis": "FREE STEEM!", + "returning_users": "Utenti di ritorno:", + "join_our": "Unisciti al nostro" + }, + "chainvalidation_js": { + "account_name_should": "Il nome account", + "not_be_empty": "non deve essere vuoto.", + "be_longer": "essere più lungo.", + "be_shorter": "essere più corto.", + "each_account_segment_should": "Ogni segmento dell'account dovrebbe", + "start_with_a_letter": "iniziare con una lettera.", + "have_only_letters_digits_or_dashes": + "avere solo lettere, cifre o trattini.", + "have_only_one_dash_in_a_row": "avere solo un trattino per riga. ", + "end_with_a_letter_or_digit": "terminare con una lettera o una cifra.", + "verified_exchange_no_memo": + "Devi includere una memo per il trasferimento." + }, + "settings_jsx": { + "invalid_url": "URL invalido", + "name_is_too_long": "Nome troppo lungo", + "name_must_not_begin_with": "Il nome non deve iniziare con @", + "about_is_too_long": "About troppo lungo ", + "location_is_too_long": "Luogo è troppo lungo", + "website_url_is_too_long": "L'url del sito è troppo lunga ", + "public_profile_settings": "Impostazioni profilo pubblico", + "private_post_display_settings": + "Impostazioni di Visualizzazione del Post Privato", + "not_safe_for_work_nsfw_content": + "Contenuto inappropriato da visualizzare da un luogo di lavoro (NSFW)", + "always_hide": "Nascondi sempre", + "always_warn": "Avverti sempre", + "always_show": "Mostra sempre", + "muted_users": "Utenti mutati", + "update": "Aggiornamento", + "profile_image_url": "Url immagine profilo", + "cover_image_url": "Url immagine di copertina", + "profile_name": "Mostra nome", + "profile_about": "About", + "profile_location": "Località", + "profile_website": "Sito" + }, + "transfer_jsx": { + "amount_is_in_form": "La quantita deve essere della forma 99999.999", + "insufficient_funds": "Fondi insufficienti", + "use_only_3_digits_of_precison": "Usare solo 3 cifre di precisione", + "send_to_account": "Invia a un account", + "asset": "Asset", + "this_memo_is_private": "Questo memo è privato", + "this_memo_is_public": "Questo memo è pubblico", + "convert_to_VESTING_TOKEN": "Converti in %(VESTING_TOKEN)s", + "balance_subject_to_3_day_withdraw_waiting_period": + "Saldo soggetto a 3 giorni di attesa per il prelievo.", + "move_funds_to_another_account": + "Sposta i fondi verso un altro account %(APP_NAME)s", + "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": + "Proteggi i fondi richiedendo un periodo di attesa di 3 giorni.", + "withdraw_funds_after_the_required_3_day_waiting_period": + "Preleva fondi dopo il periodo di attesa necessario di 3 giorni.", + "from": "Da", + "to": "A", + "asset_currently_collecting": + "%(asset)s attualmente percepiscono %(interest)s%% ISC.", + "beware_of_spam_and_phishing_links": + "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." + }, + "userwallet_jsx": { + "conversion_complete_tip": "Si completerà su", + "in_conversion": "%(amount)s in conversione", + "transfer_to_savings": "Sposta nei Risparmi", + "power_up": "Power Up", + "power_down": "Power Down", + "market": "Market", + "convert_to_LIQUID_TOKEN": "Converti in %(LIQUID_TOKEN)s", + "withdraw_LIQUID_TOKEN": "Preleva %(LIQUID_TOKEN)s", + "withdraw_DEBT_TOKENS": "Preleva %(DEBT_TOKENS)s", + "tokens_worth_about_1_of_LIQUID_TICKER": + "I token valgono circa $1.00 di %(LIQUID_TICKER)s, attualmente percepiscono %(sbdInterest)s%% ISC.", + "savings": "RISPARMI", + "estimated_account_value": "Valore stimato dell'account", + "next_power_down_is_scheduled_to_happen": + "Il prossimo power downo è programmato per avvenire", + "transfers_are_temporary_disabled": + "I trasferimenti sono temporaneamente disabilitati.", + "history": "CRONOLOGIA", + "redeem_rewards": "Riscuoti le ricompense (Trasferisci sul conto)", + "buy_steem_or_steem_power": "Compra STEEM o STEEM POWER" + }, + "powerdown_jsx": { + "power_down": "Power Down", + "amount": "Somma", + "already_power_down": + "Stai già effettuando il powering down %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s pagati sin ora). Ricorda che se cambi l'ammontare del power down il pagamento programmato verrà resettato.", + "delegating": + "Stai delegando %(AMOUNT)s %(LIQUID_TICKER)s. Questa somma sarà bloccata e non disponibile per il power down sino a quando la delega non sarà rimossa e non sia passato un intero intervallo di ricompensa.", + "per_week": "È ~%(AMOUNT)s %(LIQUID_TICKER)s a settimana.", + "warning": + "Lasciare meno di %(AMOUNT)s %(VESTING_TOKEN)s sul tuo account è sconsigliato e potrebbe renderlo inutilizzabile.", + "error": "Impossibile effettuare il power down (ERROR: %(MESSAGE)s)" + }, + "checkloginowner_jsx": { + "your_password_permissions_were_reduced": + "I tuoi permessi delle password sono stati ridotti", + "if_you_did_not_make_this_change": + "Se non hai fatto questo cambio per favore", + "ownership_changed_on": "Proprietà Cambiata Su", + "deadline_for_recovery_is": "Il termine per il ripristino è", + "i_understand_dont_show_again": "Ho capito, non mostrare nuovamente" } - }, - "userkeys_jsx": { - "public": "Pubblico", - "private": "Privato", - "public_something_key": "%(key)sChiave Pubblica", - "private_something_key": "%(key)sChiave Privata", - "posting_key_is_required_it_should_be_different": "La chiave di pubblicazione si usa per pubblicare e votare. Dovrebbe essere diversa dalla chiave attiva e dalla chiave proprietaria. ", - "the_active_key_is_used_to_make_transfers_and_place_orders": "La chiave attiva si usa per effettuare trasferimenti e per piazzare ordini nel market interno. ", - "the_owner_key_is_required_to_change_other_keys": "La chiave proprietaria è la chiave principale dell'account e serve a cambiare le altre chiavi. ", - "the_private_key_or_password_should_be_kept_offline": "Raccomandiamo di conservare offline la chiave privata e la password della chiave proprietaria. ", - "the_memo_key_is_used_to_create_and_read_memos": "La chiave memo si usa per creare e leggere i memo. " - }, - "suggestpassword_jsx": { - "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": "%(APP_NAME)snon può recuperare la password. Conserva questa pagina in un luogo sicuro, come ad esempio una cassaforte o una cassetta di sicurezza. ", - "APP_NAME_password_backup": "Password Backup di %(APP_NAME)s", - "APP_NAME_password_backup_required": "Password Backup di %(APP_NAME)s (richiesto)", - "after_printing_write_down_your_user_name": "Dopo la stampa, scrivi il tuo nome utente. " - }, - "converttosteem_jsx": { - "your_existing_DEBT_TOKEN_are_liquid_and_transferable": "I tuoi %(DEBT_TOKEN)s sono liquidi e trasferibili. Invece se desiderassi scambiare %(DEBT_TOKEN)s direttamente in questo sito su %(link)s o trasferirli su un mercato esterno.", - "this_is_a_price_feed_conversion": "Questa è una conversione di prezzo. L'attesa di 3,5 giorni è necessaria per evitare di abusare della media dei prezzi.", - "convert_to_LIQUID_TOKEN": "Converti in %(LIQUID_TOKEN)s", - "DEBT_TOKEN_will_be_unavailable": "Questa azione avverrà tra 3,5 giorni da adesso e non può essere cancellata. Questi%(DEBT_TOKEN)s diverranno subito indisponibili" - }, - "tips_js": { - "liquid_token": "Token scambiabili che possono essere trasferiti ovunque in qualsiasi momento.
        %(LIQUID_TOKEN)s possono essere convertiti in %(VESTING_TOKEN)s con un processo chiamato powering up.", - "influence_token": "Token che ti danno più controllo sui pagamenti dei post e ti consentono di guadagnare sulle ricompense da curatore.", - "estimated_value": "Il valore stimato si basa sul valore medio di %(LIQUID_TOKEN)s in dollari US. ", - "non_transferable": "%(VESTING_TOKEN)s non è trasferibile e richiede 3 mesi, (13 pagamenti) per riconvertirli in %(LIQUID_TOKEN)s.", - "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": "I %(VESTING_TOKEN)s convertiti possono essere spediti a te stesso o a qualcun altro ma non possono non ritrasferirli senza essere convertirli di nuovo in %(LIQUID_TOKEN)s.", - "part_of_your_steem_power_is_currently_delegated": "Parte del tuo STEEM POWER ti è attualmente delegato. La delegazione è donata per l'influenza o per aiutare i nuovi utenti ad eseguire le azioni su steemit. Il quantitativo delegato può fluttuare." - }, - "promote_post_jsx": { - "promote_post": "Promuovi post", - "spend_your_DEBT_TOKEN_to_advertise_this_post": "Spendi %(DEBT_TOKEN)s per pubblicizzare questo post nella sezione dei contenuti promossi.", - "you_successfully_promoted_this_post": "Hai promosso questo post con successo", - "this_post_was_hidden_due_to_low_ratings": "Questo post è stato nascosto a causa della bassa reputazione" - }, - "about_jsx": { - "about_app": "About %(APP_NAME)s", - "about_app_details": "%(APP_NAME)s è una piattaforma di social media dove tutti vengono ricompensati per la creazione e la cura di contenuti. Utilizza un complesso sistema a punti digitali, chiamato Steem che possiede un valore effettivo per le ricompense digitali attraverso la scoperta dei prezzi di mercato e la liquidità. ", - "learn_more_at_app_url": "Scopri di più %(APP_URL)s", - "resources": "Risorse" - }, - "markdownviewer_jsx": { - "images_were_hidden_due_to_low_ratings": "Le immagini sono state nascoste a causa della bassa reputazione." - }, - "postsummary_jsx": { - "resteemed": "condiviso", - "resteemed_by": "Condiviso da", - "reveal_it": "mostra", - "adjust_your": "sistema il tuo", - "display_preferences": "mostrare le tue preferenze", - "create_an_account": "creare un accout", - "to_save_your_preferences": "per salvare le tue preferenze" - }, - "posts_index": { - "empty_feed_1": "Sembra che tu non stia ancora seguendo nessuno. ", - "empty_feed_2": "Se hai da poco aggiunto nuovi utenti da seguire, quando ci saranno nuovi contenuti disponibili li vedrai nel tuo feed personalizzato.", - "empty_feed_3": "Esplora gli Articoli in Trending", - "empty_feed_4": "Leggi la Guida di Avvio Rapido", - "empty_feed_5": "Leggi le FAQ" - }, - "transferhistoryrow_jsx": { - "to_savings": "ai risparmi", - "from_savings": "dai risparmi", - "cancel_transfer_from_savings": "Cancella il trasferimento dai risparmi", - "stop_power_down": "Interrompi il Power Down", - "start_power_down_of": "Avvia il Power Down di", - "receive_interest_of": "Ricevi l'interesse di" - }, - "savingswithdrawhistory_jsx": { - "cancel_this_withdraw_request": "Cancellare questa richiesta di prelevamento?", - "pending_savings_withdrawals": "PRELIEVI DEI RISPARMI IN SOSPESO", - "withdraw": "Ritira %(amount)s", - "to": "a %(to)s", - "from_to": "da %(from)s a %(to)s" - }, - "explorepost_jsx": { - "copied": "Copiato!", - "copy": "Copia", - "alternative_sources": "Fonti alternative" - }, - "header_jsx": { - "home": "Home", - "create_a_post": "Crea un post", - "change_account_password": "Cambia la password dell'account", - "create_account": "Crea un account", - "stolen_account_recovery": "Recupero account rubato", - "people_following": "Persone che seguono", - "people_followed_by": "Persone seguite da ", - "curation_rewards_by": "Ricompense da curatore da", - "author_rewards_by": "Ricompense da autore da", - "replies_to": "Risposte a ", - "comments_by": "Commenti di" - }, - "loginform_jsx": { - "you_need_a_private_password_or_key": "Hai bisogno di una password privata o di una chiave (non di una chiave pubblica)", - "cryptography_test_failed": "Test di crittofrafia fallito", - "unable_to_log_you_in": "Non è possibile effettuare il log in con questo browser.", - "the_latest_versions_of": "Le ultime versioni di ", - "are_well_tested_and_known_to_work_with": "sono testate e conosciute per funzionare con %(APP_URL)s.", - "due_to_server_maintenance": "A causa della manutenzione del server siamo in modalità sola lettura. Ci dispiace per l'inconveniente.", - "login_to_vote": "Effettua il login per votare ", - "login_to_post": "Effettua il login per postare", - "login_to_comment": "Effettua il login per commentare", - "posting": "Post", - "active_or_owner": "Attivo o proprietario", - "this_password_is_bound_to_your_account_owner_key": "Questa password è collegata alla chiave proprietaria del tuo account e non può essere usata per effettuare il login su questo sito. ", - "however_you_can_use_it_to": "Nonostante questo, puoi usarla per", - "update_your_password": "aggiornare la tua password", - "to_obtain_a_more_secure_set_of_keys": "per ottenere un set di chiavi più sicuro. ", - "this_password_is_bound_to_your_account_active_key": "Questa password è collegata alla chiave attiva del tuo account e non può essere usata per effettuare il login su questa pagina. ", - "you_may_use_this_active_key_on_other_more": "Puoi usare la chiave attiva su altre pagine di sicurezza come le pagine Wallet o Market. ", - "you_account_has_been_successfully_created": "Il tuo account è stato creato con successo! ", - "you_account_has_been_successfully_recovered": "Il tuo account è stato ripristinato con successo!", - "password_update_succes": "La password dell'account %(accountName)s è stata aggiornata con successo", - "password_info": "La password o la chiave privata è stata inserita erroneamente. C'è probabilmente un errore di battitura o un inserimento di dati errato. Suggerimento: Una password o una chiave privata generata da Steemit non conterrà mai i caratteri 0 (zero), O (o maiuscola), I (i maiuscola) e l (l minuscola).", - "enter_your_username": "Inserisci il tuo username", - "password_or_wif": "Password o WIF", - "this_operation_requires_your_key_or_master_password": "Per eseguire l'operazione è necessaria la propria chiave %(authType)so Master password. ", - "keep_me_logged_in": "Voglio restare connesso", - "amazing_community": "una comunità incredibile", - "to_comment_and_reward_others": "per commentare e premiare gli altri. ", - "sign_up_get_steem": "Sign up. Get STEEM", - "signup_button": "Sign up now to earn ", - "signup_button_emphasis": "FREE STEEM!", - "returning_users": "Utenti di ritorno:", - "join_our": "Unisciti al nostro" - }, - "chainvalidation_js": { - "account_name_should": "Il nome account", - "not_be_empty": "non deve essere vuoto.", - "be_longer": "essere più lungo.", - "be_shorter": "essere più corto.", - "each_account_segment_should": "Ogni segmento dell'account dovrebbe", - "start_with_a_letter": "iniziare con una lettera.", - "have_only_letters_digits_or_dashes": "avere solo lettere, cifre o trattini.", - "have_only_one_dash_in_a_row": "avere solo un trattino per riga. ", - "end_with_a_letter_or_digit": "terminare con una lettera o una cifra.", - "verified_exchange_no_memo": "Devi includere una memo per il trasferimento." - }, - "settings_jsx": { - "invalid_url": "URL invalido", - "name_is_too_long": "Nome troppo lungo", - "name_must_not_begin_with": "Il nome non deve iniziare con @", - "about_is_too_long": "About troppo lungo ", - "location_is_too_long": "Luogo è troppo lungo", - "website_url_is_too_long": "L'url del sito è troppo lunga ", - "public_profile_settings": "Impostazioni profilo pubblico", - "private_post_display_settings": "Impostazioni di Visualizzazione del Post Privato", - "not_safe_for_work_nsfw_content": "Contenuto inappropriato da visualizzare da un luogo di lavoro (NSFW)", - "always_hide": "Nascondi sempre", - "always_warn": "Avverti sempre", - "always_show": "Mostra sempre", - "muted_users": "Utenti mutati", - "update": "Aggiornamento", - "profile_image_url": "Url immagine profilo", - "cover_image_url": "Url immagine di copertina", - "profile_name": "Mostra nome", - "profile_about": "About", - "profile_location": "Località", - "profile_website": "Sito" - }, - "transfer_jsx": { - "amount_is_in_form": "La quantita deve essere della forma 99999.999", - "insufficient_funds": "Fondi insufficienti", - "use_only_3_digits_of_precison": "Usare solo 3 cifre di precisione", - "send_to_account": "Invia a un account", - "asset": "Asset", - "this_memo_is_private": "Questo memo è privato", - "this_memo_is_public": "Questo memo è pubblico", - "convert_to_VESTING_TOKEN": "Converti in %(VESTING_TOKEN)s", - "balance_subject_to_3_day_withdraw_waiting_period": "Saldo soggetto a 3 giorni di attesa per il prelievo.", - "move_funds_to_another_account": "Sposta i fondi verso un altro account %(APP_NAME)s", - "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": "Proteggi i fondi richiedendo un periodo di attesa di 3 giorni.", - "withdraw_funds_after_the_required_3_day_waiting_period": "Preleva fondi dopo il periodo di attesa necessario di 3 giorni.", - "from": "Da", - "to": "A", - "asset_currently_collecting": "%(asset)s attualmente percepiscono %(interest)s%% ISC.", - "beware_of_spam_and_phishing_links": "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." - }, - "userwallet_jsx": { - "conversion_complete_tip": "Si completerà su", - "in_conversion": "%(amount)s in conversione", - "transfer_to_savings": "Sposta nei Risparmi", - "power_up": "Power Up", - "power_down": "Power Down", - "market": "Market", - "convert_to_LIQUID_TOKEN": "Converti in %(LIQUID_TOKEN)s", - "withdraw_LIQUID_TOKEN": "Preleva %(LIQUID_TOKEN)s", - "withdraw_DEBT_TOKENS": "Preleva %(DEBT_TOKENS)s", - "tokens_worth_about_1_of_LIQUID_TICKER": "I token valgono circa $1.00 di %(LIQUID_TICKER)s, attualmente percepiscono %(sbdInterest)s%% ISC.", - "savings": "RISPARMI", - "estimated_account_value": "Valore stimato dell'account", - "next_power_down_is_scheduled_to_happen": "Il prossimo power downo è programmato per avvenire", - "transfers_are_temporary_disabled": "I trasferimenti sono temporaneamente disabilitati.", - "history": "CRONOLOGIA", - "redeem_rewards": "Riscuoti le ricompense (Trasferisci sul conto)", - "buy_steem_or_steem_power": "Compra STEEM o STEEM POWER" - }, - "powerdown_jsx": { - "power_down": "Power Down", - "amount": "Somma", - "already_power_down": "Stai già effettuando il powering down %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s pagati sin ora). Ricorda che se cambi l'ammontare del power down il pagamento programmato verrà resettato.", - "delegating": "Stai delegando %(AMOUNT)s %(LIQUID_TICKER)s. Questa somma sarà bloccata e non disponibile per il power down sino a quando la delega non sarà rimossa e non sia passato un intero intervallo di ricompensa.", - "per_week": "È ~%(AMOUNT)s %(LIQUID_TICKER)s a settimana.", - "warning": "Lasciare meno di %(AMOUNT)s %(VESTING_TOKEN)s sul tuo account è sconsigliato e potrebbe renderlo inutilizzabile.", - "error": "Impossibile effettuare il power down (ERROR: %(MESSAGE)s)" - }, - "checkloginowner_jsx": { - "your_password_permissions_were_reduced": "I tuoi permessi delle password sono stati ridotti", - "if_you_did_not_make_this_change": "Se non hai fatto questo cambio per favore", - "ownership_changed_on": "Proprietà Cambiata Su", - "deadline_for_recovery_is": "Il termine per il ripristino è", - "i_understand_dont_show_again": "Ho capito, non mostrare nuovamente" - } } diff --git a/src/app/locales/ko.json b/src/app/locales/ko.json index c5085c60b..24c260a8d 100644 --- a/src/app/locales/ko.json +++ b/src/app/locales/ko.json @@ -1,653 +1,773 @@ { - "g": { - "age": "나이", - "amount": "Amount", - "and": "and", - "are_you_sure": "Are you sure?", - "ask": "Ask", - "balance": "Balance", - "balances": "Balances", - "bid": "Bid", - "blog": "블로그", - "browse": "Browse", - "buy": "구매", - "buy_or_sell": "Buy or Sell", - "by": "by", - "cancel": "취소", - "change_password": "비밀번호 변경", - "choose_language": "언어 선택", - "clear": "확인", - "close": "닫기", - "collapse_or_expand": "닫기/펼치기", - "comments": "나의댓글", - "confirm": "확인", - "convert": "변환", - "date": "날자", - "delete": "삭제", - "dismiss": "확인", - "edit": "수정", - "email": "Email", - "feed": "피드", - "follow": "팔로우", - "for": " for ", - "from": " from ", - "go_back": "Back", - "hide": "Hide", - "in": "in", - "in_reply_to": "in reply to", - "insufficient_balance": "잔고 부족", - "invalid_amount": "Invalid amount", - "joined": "Joined", - "loading": "Loading", - "login": "로그인", - "logout": "로그아웃", - "memo": "메모", - "mute": "뮤트", - "myblog": "내블로그", - "mycomments": "나의댓글", - "myreplies": "내게달린댓글", - "new": "최신글", - "newer": "Newer", - "next": "Next", - "no": "No", - "ok": "Ok", - "older": "Older", - "or": "or", - "order_placed": "Order placed", - "password": "비밀번호", - "payouts": "글보상", - "permissions": "권한", - "phishy_message": "Link expanded to plain text; beware of a potential phishing attempt", - "post": "글쓰기", - "post_as": "Post as", - "posts": "Posts", - "powered_up_100": "Powered Up 100%%", - "preview": "Preview", - "previous": "Previous", - "price": "Price", - "print": "Print", - "promote": "홍보", - "promoted": "홍보글", - "re": "RE", - "re_to": "RE: %(topic)s", - "recent_password": "Recent Password", - "receive": "Receive ", - "remove": "Remove", - "remove_vote": "보팅취소", - "replied_to": "replied to %(account)s", - "replies": "받은댓글", - "reply": "댓글달기", - "reply_count": { - "zero": "댓글 없음", - "one": "하나의 댓글", - "other": "%(count)개의 댓글" - }, - "reputation": "Reputation", - "reveal_comment": "Reveal Comment", - "request": "request", - "required": "Required", - "rewards": "수익", - "save": "Save", - "saved": "Saved", - "search": "Search", - "sell": "Sell", - "settings": "설정", - "share_this_post": "Share this post", - "show": "Show", - "sign_in": "로그인", - "sign_up": "회원가입", - "since": "since", - "submit": "Submit", - "power_up": "파워 업", - "submit_a_story": "새글", - "tag": "Tag", - "to": " to ", - "topics": "Topics", - "toggle_nightmode": "야간모드전환", - "all_tags": "All tags", - "transfer": "Transfer ", - "trending_topics": "Trending Topics", - "type": "Type", - "unfollow": "언팔로우", - "unmute": "뮤트취소", - "unknown": "Unknown", - "upvote": "보팅", - "upvote_post": "이 글에 보팅", - "username": "사용자 이름", - "version": "버전", - "vote": "보트", - "votes": "보트", - "wallet": "지갑", - "warning": "경고", - "yes": "예", - "posting": "포스팅", - "owner": "소유자", - "active": "액티브", - "account_not_found": "계정을 찾을 수 없습니다", - "this_is_wrong_password": "잘못된 비밀번호 입니다", - "do_you_need_to": "Do you need to", - "account_name": "Account Name", - "recover_your_account": "recover your account", - "reset_usernames_password": "Reset %(username)s's Password", - "this_will_update_usernames_authtype_key": "This will update %(username)s %(authType)s key", - "passwords_do_not_match": "Passwords do not match", - "you_need_private_password_or_key_not_a_public_key": "You need a private password or key (not a public key)", - "the_rules_of_APP_NAME": { - "one": "The first rule of %(APP_NAME)s is: Do not lose your password.", - "second": "The second rule of %(APP_NAME)s is: Do not lose your password.", - "third": "The third rule of %(APP_NAME)s is: We cannot recover your password.", - "fourth": "The fourth rule: If you can remember the password, it's not secure.", - "fifth": "The fifth rule: Use only randomly-generated passwords.", - "sixth": "The sixth rule: Do not tell anyone your password.", - "seventh": "The seventh rule: Always back up your password." - }, - "recover_password": "Recover Account", - "current_password": "Current Password", - "generated_password": "Generated Password", - "backup_password_by_storing_it": "Back it up by storing in your password manager or a text file", - "enter_account_show_password": "Enter a valid account name to show the password", - "click_to_generate_password": "Click to generate password", - "re_enter_generate_password": "Re-enter Generated Password", - "understand_that_APP_NAME_cannot_recover_password": "I understand that %(APP_NAME)s cannot recover lost passwords", - "i_saved_password": "I have securely saved my generated password", - "update_password": "Update Password", - "confirm_password": "Confirm Password", - "account_updated": "Account Updated", - "password_must_be_characters_or_more": "Password must be %(amount)s characters or more", - "need_password_or_key": "You need a private password or key (not a public key)", - "login_to_see_memo": "login to see memo", - "new_password": "New Password", - "incorrect_password": "Incorrect password", - "username_does_not_exist": "Username does not exist", - "account_name_should_start_with_a_letter": "Account name should start with a letter.", - "account_name_should_be_shorter": "Account name should be shorter.", - "account_name_should_be_longer": "Account name should be longer.", - "account_name_should_have_only_letters_digits_or_dashes": "Account name should have only letters, digits, or dashes.", - "cannot_increase_reward_of_post_within_the_last_minute_before_payout": "Cannot increase reward of post within the last minute before payout", - "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": "vote currently exists, user must be indicate a desire to reject witness", - "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": "Only one Steem account allowed per IP address every 10 minutes", - "resteem_this_post": "Resteem This Post", - "reblog": "Resteem", - "write_your_story": "Write your story", - "remember_voting_and_posting_key": "Remember voting & posting key", - "auto_login_question_mark": "Auto login?", - "hide_private_key": "Hide private key", - "show_private_key": "Show private key", - "login_to_show": "Login to show", - "not_valid_email": "Not valid email", - "thank_you_for_being_an_early_visitor_to_APP_NAME": "Thank you for being an early visitor to %(APP_NAME)s. We will get back to you at the earliest possible opportunity.", - "author_rewards": "저자 수익", - "curation_rewards": "큐레이션 수익", - "sorry_your_reddit_account_doesnt_have_enough_karma": "Sorry, your Reddit account doesn't have enough Reddit Karma to qualify for a free sign up. Please add your email for a place on the waiting list", - "register_with_facebook": "Register with Facebook", - "or_click_the_button_below_to_register_with_facebook": "Or click the button below to register with Facebook", - "server_returned_error": "server returned error", - "APP_NAME_support": "%(APP_NAME)s Support", - "please_email_questions_to": "Please email your questions to", - "next_7_strings_single_block": { - "authors_get_paid_when_people_like_you_upvote_their_post": "Authors get paid when people like you upvote their post", - "if_you_enjoyed_what_you_read_earn_amount": "If you enjoyed what you read here, create your account today and start earning FREE STEEM!", - "free_steem": "FREE STEEM!", - "sign_up_earn_steem": "Sign up now to earn " - }, - "next_3_strings_together": { - "show_more": "Show more", - "show_less": "Show fewer", - "value_posts": "low value posts" - }, - "read_only_mode": "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", - "tags_and_topics": "태그", - "show_more_topics": "모든 태그 보기", - "basic": "Basic", - "advanced": "Advanced", - "views": { - "zero": "No Views", - "one": "%(count)s View", - "other": "%(count)s Views" - }, - "responses": { - "zero": "No Responses", - "one": "%(count)s Response", - "other": "%(count)s Responses" - }, - "post_key_warning": { - "confirm": "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", - "warning": "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", - "checkbox": "I understand" - } - }, - "navigation": { - "about": "About", - "explore": "Explore", - "APP_NAME_whitepaper": "%(APP_NAME)s Whitepaper", - "buy_LIQUID_TOKEN": "Buy %(LIQUID_TOKEN)s", - "sell_LIQUID_TOKEN": "Sell %(LIQUID_TOKEN)s", - "currency_market": "Currency Market", - "stolen_account_recovery": "Stolen Accounts Recovery", - "change_account_password": "Change Account Password", - "witnesses": "Witnesses", - "vote_for_witnesses": "Vote for Witnesses", - "privacy_policy": "Privacy Policy", - "terms_of_service": "Terms of Service", - "sign_up": "Join", - "learn_more": "Learn More", - "welcome": "Welcome", - "faq": "FAQ", - "shop": "The Steemit Shop", - "chat": "Steemit Chat", - "app_center": "Steemit App Center", - "api_docs": "Steemit API Docs", - "bluepaper": "Steem Bluepaper", - "whitepaper": "Steem Whitepaper", - "intro_tagline": "머니 톡스", - "intro_paragraph": "Your voice is worth something. Join the community that pays you to post and curate high quality content." - }, - "main_menu": { - "hot": "인기글", - "trending": "추천글" - }, - "reply_editor": { - "shorten_title": "Shorten title", - "exceeds_maximum_length": "Exceeds maximum length (%(maxKb)sKB)", - "including_the_category": " (including the category '%(rootCategory)s')", - "use_limited_amount_of_tags": "You have %(tagsLength)s tags total%(includingCategory)s. Please use only 5 in your post and category line.", - "are_you_sure_you_want_to_clear_this_form": "Are you sure you want to clear this form?", - "uploading": "Uploading", - "draft_saved": "Draft saved.", - "editor": "Editor", - "insert_images_by_dragging_dropping": "이미지 추가를 위해서는 드래그&드롭, ", - "pasting_from_the_clipboard": "클립보드에서 분여넣기, ", - "selecting_them": "혹은 직접 업로드 하세요.", - "image_upload": "사진 업로드", - "power_up_100": "100%% 스팀파워로 받음", - "default_50_50": "스팀파워 50%% + 스팀달러 50%%)", - "decline_payout": "수익을 스팀 커뮤니티에 기부", - "check_this_to_auto_upvote_your_post": "Check this to auto-upvote your post", - "markdown_styling_guide": "Markdown Styling Guide", - "or_by": "or by", - "title": "Title", - "update_post": "Update Post", - "markdown_not_supported": "Markdown is not supported here" - }, - "category_selector_jsx": { - "tag_your_story": "최대 5개의 태그를 입력하세요. 메인 태그는 가장 먼저 입력하세요.", - "select_a_tag": "태그를 선택하세요.", - "maximum_tag_length_is_24_characters": "태그는 24 글자까지 가능합니다.", - "use_limited_amount_of_categories": "태그는 최대 %(amount)s 개 까지 입력 가능합니다.", - "use_only_lowercase_letters": "소문자만 사용해 주세요.", - "use_one_dash": "대쉬는 한개만 사용 가능합니다.", - "use_spaces_to_separate_tags": "태그와 태그 사이는 공백으로 구분 하세요.", - "use_only_allowed_characters": "소문자, 숫자 그리고 대쉬만 사용 가능합니다.", - "must_start_with_a_letter": "태그는 반드시 문자로 시작해야 합니다.", - "must_end_with_a_letter_or_number": "태그는 반드시 문자나 숫자로 끝나야 합니다." - }, - "postfull_jsx": { - "this_post_is_not_available_due_to_a_copyright_claim": "This post is not available due to a copyright claim.", - "share_on_facebook": "Share on Facebook", - "share_on_twitter": "Share on Twitter", - "share_on_linkedin": "Share on Linkedin", - "recent_password": "Recent Password", - "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": "In 3.5 days, convert %(amount)s %(DEBT_TOKEN)s into %(LIQUID_TOKEN)s", - "view_the_full_context": "View the full context", - "view_the_direct_parent": "View the direct parent", - "you_are_viewing_a_single_comments_thread_from": "You are viewing a single comment's thread from" - }, - "market_jsx": { - "action": "Action", - "date_created": "Date Created", - "last_price": "Last price", - "24h_volume": "24h volume", - "spread": "Spread", - "total": "Total", - "available": "Available", - "lowest_ask": "Lowest ask", - "highest_bid": "Highest bid", - "buy_orders": "Buy Orders", - "sell_orders": "Sell Orders", - "trade_history": "Trade History", - "open_orders": "Open Orders", - "sell_amount_for_atleast": "Sell %(amount_to_sell)s for at least %(min_to_receive)s (%(effectivePrice)s)", - "buy_atleast_amount_for": "Buy at least %(min_to_receive)s for %(amount_to_sell)s (%(effectivePrice)s)", - "price_warning_above": "This price is well above the current market price of %(marketPrice)s, are you sure?", - "price_warning_below": "This price is well below the current market price of %(marketPrice)s, are you sure?", - "order_cancel_confirm": "Cancel order %(order_id)s from %(user)s?", - "order_cancelled": "Order %(order_id)s cancelled.", - "higher": "Higher", - "lower": "Lower", - "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" - }, - "recoveraccountstep1_jsx": { - "begin_recovery": "Begin Recovery", - "not_valid": "Not valid", - "account_name_is_not_found": "Account name is not found", - "unable_to_recover_account_not_change_ownership_recently": "We are unable to recover this account, it has not changed ownership recently.", - "password_not_used_in_last_days": "This password was not used on this account in the last 30 days.", - "request_already_submitted_contact_support": "Your request has been already submitted and we are working on it. Please contact %(SUPPORT_EMAIL)s for the status of your request.", - "recover_account_intro": "From time to time, a Steemian's owner key may be compromised. Stolen Account Recovery gives the rightful account owner 30 days to recover their account from the moment the thief changed their owner key. Stolen Account Recovery can only be used on %(APP_URL)s if the account owner had previously listed '%(APP_NAME)s' as their account trustee and complied with %(APP_NAME)s's Terms of Service.", - "login_with_facebook_or_reddit_media_to_verify_identity": "Please login with Facebook or Reddit to verify your identity", - "login_with_social_media_to_verify_identity": "Please login with %(provider)s to verify your identity", - "enter_email_toverify_identity": "We need to verify your identity. Please enter your email address below to begin the verification.", - "continue_with_email": "Continue with Email", - "thanks_for_submitting_request_for_account_recovery": "Thanks for submitting your request for Account Recovery using %(APP_NAME)s's blockchain-based multi factor authentication. We will respond to you as quickly as possible, however, please expect there may be some delay in response due to high volume of emails. Please be prepared to verify your identity.", - "recovering_account": "Recovering account", - "recover_account": "Recover Account", - "checking_account_owner": "Checking account owner", - "sending_recovery_request": "Sending recovery request", - "cant_confirm_account_ownership": "We can't confirm account ownership. Check your password", - "account_recovery_request_not_confirmed": "Account recovery request is not confirmed yet, please get back later, thank you for your patience." - }, - "user_profile": { - "unknown_account": "Unknown Account", - "user_hasnt_made_any_posts_yet": "Looks like %(name)s hasn't made any posts yet!", - "user_hasnt_started_bloggin_yet": "Looks like %(name)s hasn't started blogging yet!", - "user_hasnt_followed_anything_yet": "Looks like %(name)s might not be following anyone yet! If %(name)s recently added new users to follow, their personalized feed will populate once new content is available.", - "user_hasnt_had_any_replies_yet": "%(name)s hasn't had any replies yet", - "looks_like_you_havent_posted_anything_yet": "Looks like you haven't posted anything yet.", - "create_a_post": "Create a Post", - "explore_trending_articles": "Explore Trending Articles", - "read_the_quick_start_guide": "Read The Quick Start Guide", - "browse_the_faq": "Browse The FAQ", - "followers": "팔로워", - "this_is_users_reputations_score_it_is_based_on_history_of_votes": "This is %(name)s's reputation score.\n\nThe reputation score is based on the history of votes received by the account, and is used to hide low quality content.", - "follower_count": { - "zero": "팔로워 없음", - "one": "1명의 팔로워", - "other": "%(count)s명의 팔로워" - }, - "followed_count": { - "zero": "팔로우 없음", - "one": "1명 팔로우", - "other": "%(count)s명 팔로우" - }, - "post_count": { - "zero": "게시글 없음", - "one": "1 게시글", - "other": "%(count)s 게시글" - } - }, - "authorrewards_jsx": { - "estimated_author_rewards_last_week": "금주의 예상 저자 수익", - "author_rewards_history": "저자 수익 내역" - }, - "curationrewards_jsx": { - "estimated_curation_rewards_last_week": "금주의 큐레이션 예상 수익", - "curation_rewards_history": "큐레이션 수익 내역" - }, - "post_jsx": { - "now_showing_comments_with_low_ratings": "Now showing comments with low ratings", - "sort_order": "Sort Order", - "comments_were_hidden_due_to_low_ratings": "Comments were hidden due to low ratings" - }, - "voting_jsx": { - "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": "Flagging a post can remove rewards and make this material less visible. Some common reasons to flag", - "disagreement_on_rewards": "Disagreement on rewards", - "fraud_or_plagiarism": "Fraud or Plagiarism", - "hate_speech_or_internet_trolling": "Hate Speech or Internet Trolling", - "intentional_miss_categorized_content_or_spam": "Intentional miss-categorized content or Spam", - "pending_payout": "대기중인 보상: $%(value)s", - "payout_declined": "보상을 사양함", - "max_accepted_payout": "Max Accepted Payout $%(value)s", - "promotion_cost": "Promotion Cost $%(value)s", - "past_payouts": "Past Payouts $%(value)s", - "past_payouts_author": " - Author $%(value)s", - "past_payouts_curators": " - Curators $%(value)s", - "we_will_reset_curation_rewards_for_this_post": "will reset your curation rewards for this post", - "removing_your_vote": "Removing your vote", - "changing_to_an_upvote": "Changing to an Up-Vote", - "changing_to_a_downvote": "Changing to a Down-Vote", - "confirm_flag": "Confirm Flag", - "and_more": "and %(count)s more", - "votes_plural": { - "one": "%(count)s 보트", - "other": "%(count)s 보트" - } - }, - "witnesses_jsx": { - "witness_thread": "witness thread", - "top_witnesses": "증인 투표", - "you_have_votes_remaining": { - "zero": "You have no votes remaining", - "one": "You have 1 vote remaining", - "other": "You have %(count)s votes remaining" - }, - "you_can_vote_for_maximum_of_witnesses": "You can vote for a maximum of 30 witnesses", - "witness": "증인", - "information": "Information", - "if_you_want_to_vote_outside_of_top_enter_account_name": "If you would like to vote for a witness outside of the top 50, enter the account name below to cast a vote", - "set_witness_proxy": "You can also choose a proxy that will vote for witnesses for you. This will reset your current witness selection.", - "witness_set": "You have set a voting proxy. If you would like to re-enable manual voting, please clear your proxy.", - "witness_proxy_current": "Your current proxy is", - "witness_proxy_set": "Set proxy", - "witness_proxy_clear": "Clear proxy", - "proxy_update_error": "Your proxy was not updated" - }, - "votesandcomments_jsx": { - "no_responses_yet_click_to_respond": "No responses yet. Click to respond.", - "response_count_tooltip": { - "zero": "no responses. Click to respond.", - "one": "1 response. Click to respond.", - "other": "%(count)s responses. Click to respond." - }, - "vote_count": { - "zero": "추천 없음", - "one": "1개의 추천", - "other": "%(count)s개의 추천" + "g": { + "age": "나이", + "amount": "Amount", + "and": "and", + "are_you_sure": "Are you sure?", + "ask": "Ask", + "balance": "Balance", + "balances": "Balances", + "bid": "Bid", + "blog": "블로그", + "browse": "Browse", + "buy": "구매", + "buy_or_sell": "Buy or Sell", + "by": "by", + "cancel": "취소", + "change_password": "비밀번호 변경", + "choose_language": "언어 선택", + "clear": "확인", + "close": "닫기", + "collapse_or_expand": "닫기/펼치기", + "comments": "나의댓글", + "confirm": "확인", + "convert": "변환", + "date": "날자", + "delete": "삭제", + "dismiss": "확인", + "edit": "수정", + "email": "Email", + "feed": "피드", + "follow": "팔로우", + "for": " for ", + "from": " from ", + "go_back": "Back", + "hide": "Hide", + "in": "in", + "in_reply_to": "in reply to", + "insufficient_balance": "잔고 부족", + "invalid_amount": "Invalid amount", + "joined": "Joined", + "loading": "Loading", + "login": "로그인", + "logout": "로그아웃", + "memo": "메모", + "mute": "뮤트", + "myblog": "내블로그", + "mycomments": "나의댓글", + "myreplies": "내게달린댓글", + "new": "최신글", + "newer": "Newer", + "next": "Next", + "no": "No", + "ok": "Ok", + "older": "Older", + "or": "or", + "order_placed": "Order placed", + "password": "비밀번호", + "payouts": "글보상", + "permissions": "권한", + "phishy_message": + "Link expanded to plain text; beware of a potential phishing attempt", + "post": "글쓰기", + "post_as": "Post as", + "posts": "Posts", + "powered_up_100": "Powered Up 100%%", + "preview": "Preview", + "previous": "Previous", + "price": "Price", + "print": "Print", + "promote": "홍보", + "promoted": "홍보글", + "re": "RE", + "re_to": "RE: %(topic)s", + "recent_password": "Recent Password", + "receive": "Receive ", + "remove": "Remove", + "remove_vote": "보팅취소", + "replied_to": "replied to %(account)s", + "replies": "받은댓글", + "reply": "댓글달기", + "reply_count": { + "zero": "댓글 없음", + "one": "하나의 댓글", + "other": "%(count)개의 댓글" + }, + "reputation": "Reputation", + "reveal_comment": "Reveal Comment", + "request": "request", + "required": "Required", + "rewards": "수익", + "save": "Save", + "saved": "Saved", + "search": "Search", + "sell": "Sell", + "settings": "설정", + "share_this_post": "Share this post", + "show": "Show", + "sign_in": "로그인", + "sign_up": "회원가입", + "since": "since", + "submit": "Submit", + "power_up": "파워 업", + "submit_a_story": "새글", + "tag": "Tag", + "to": " to ", + "topics": "Topics", + "toggle_nightmode": "야간모드전환", + "all_tags": "All tags", + "transfer": "Transfer ", + "trending_topics": "Trending Topics", + "type": "Type", + "unfollow": "언팔로우", + "unmute": "뮤트취소", + "unknown": "Unknown", + "upvote": "보팅", + "upvote_post": "이 글에 보팅", + "username": "사용자 이름", + "version": "버전", + "vote": "보트", + "votes": "보트", + "wallet": "지갑", + "warning": "경고", + "yes": "예", + "posting": "포스팅", + "owner": "소유자", + "active": "액티브", + "account_not_found": "계정을 찾을 수 없습니다", + "this_is_wrong_password": "잘못된 비밀번호 입니다", + "do_you_need_to": "Do you need to", + "account_name": "Account Name", + "recover_your_account": "recover your account", + "reset_usernames_password": "Reset %(username)s's Password", + "this_will_update_usernames_authtype_key": + "This will update %(username)s %(authType)s key", + "passwords_do_not_match": "Passwords do not match", + "you_need_private_password_or_key_not_a_public_key": + "You need a private password or key (not a public key)", + "the_rules_of_APP_NAME": { + "one": + "The first rule of %(APP_NAME)s is: Do not lose your password.", + "second": + "The second rule of %(APP_NAME)s is: Do not lose your password.", + "third": + "The third rule of %(APP_NAME)s is: We cannot recover your password.", + "fourth": + "The fourth rule: If you can remember the password, it's not secure.", + "fifth": "The fifth rule: Use only randomly-generated passwords.", + "sixth": "The sixth rule: Do not tell anyone your password.", + "seventh": "The seventh rule: Always back up your password." + }, + "recover_password": "Recover Account", + "current_password": "Current Password", + "generated_password": "Generated Password", + "backup_password_by_storing_it": + "Back it up by storing in your password manager or a text file", + "enter_account_show_password": + "Enter a valid account name to show the password", + "click_to_generate_password": "Click to generate password", + "re_enter_generate_password": "Re-enter Generated Password", + "understand_that_APP_NAME_cannot_recover_password": + "I understand that %(APP_NAME)s cannot recover lost passwords", + "i_saved_password": "I have securely saved my generated password", + "update_password": "Update Password", + "confirm_password": "Confirm Password", + "account_updated": "Account Updated", + "password_must_be_characters_or_more": + "Password must be %(amount)s characters or more", + "need_password_or_key": + "You need a private password or key (not a public key)", + "login_to_see_memo": "login to see memo", + "new_password": "New Password", + "incorrect_password": "Incorrect password", + "username_does_not_exist": "Username does not exist", + "account_name_should_start_with_a_letter": + "Account name should start with a letter.", + "account_name_should_be_shorter": "Account name should be shorter.", + "account_name_should_be_longer": "Account name should be longer.", + "account_name_should_have_only_letters_digits_or_dashes": + "Account name should have only letters, digits, or dashes.", + "cannot_increase_reward_of_post_within_the_last_minute_before_payout": + "Cannot increase reward of post within the last minute before payout", + "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": + "vote currently exists, user must be indicate a desire to reject witness", + "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": + "Only one Steem account allowed per IP address every 10 minutes", + "resteem_this_post": "Resteem This Post", + "reblog": "Resteem", + "write_your_story": "Write your story", + "remember_voting_and_posting_key": "Remember voting & posting key", + "auto_login_question_mark": "Auto login?", + "hide_private_key": "Hide private key", + "show_private_key": "Show private key", + "login_to_show": "Login to show", + "not_valid_email": "Not valid email", + "thank_you_for_being_an_early_visitor_to_APP_NAME": + "Thank you for being an early visitor to %(APP_NAME)s. We will get back to you at the earliest possible opportunity.", + "author_rewards": "저자 수익", + "curation_rewards": "큐레이션 수익", + "sorry_your_reddit_account_doesnt_have_enough_karma": + "Sorry, your Reddit account doesn't have enough Reddit Karma to qualify for a free sign up. Please add your email for a place on the waiting list", + "register_with_facebook": "Register with Facebook", + "or_click_the_button_below_to_register_with_facebook": + "Or click the button below to register with Facebook", + "server_returned_error": "server returned error", + "APP_NAME_support": "%(APP_NAME)s Support", + "please_email_questions_to": "Please email your questions to", + "next_7_strings_single_block": { + "authors_get_paid_when_people_like_you_upvote_their_post": + "Authors get paid when people like you upvote their post", + "if_you_enjoyed_what_you_read_earn_amount": + "If you enjoyed what you read here, create your account today and start earning FREE STEEM!", + "free_steem": "FREE STEEM!", + "sign_up_earn_steem": "Sign up now to earn " + }, + "next_3_strings_together": { + "show_more": "Show more", + "show_less": "Show fewer", + "value_posts": "low value posts" + }, + "read_only_mode": + "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", + "tags_and_topics": "태그", + "show_more_topics": "모든 태그 보기", + "basic": "Basic", + "advanced": "Advanced", + "views": { + "zero": "No Views", + "one": "%(count)s View", + "other": "%(count)s Views" + }, + "responses": { + "zero": "No Responses", + "one": "%(count)s Response", + "other": "%(count)s Responses" + }, + "post_key_warning": { + "confirm": + "You are about to publish a STEEM private key or master password. You will probably lose control of the associated account and all its funds.", + "warning": + "Legitimate users, including employees of Steemit Inc., will never ask you for a private key or master password.", + "checkbox": "I understand" + } + }, + "navigation": { + "about": "About", + "explore": "Explore", + "APP_NAME_whitepaper": "%(APP_NAME)s Whitepaper", + "buy_LIQUID_TOKEN": "Buy %(LIQUID_TOKEN)s", + "sell_LIQUID_TOKEN": "Sell %(LIQUID_TOKEN)s", + "currency_market": "Currency Market", + "stolen_account_recovery": "Stolen Accounts Recovery", + "change_account_password": "Change Account Password", + "witnesses": "Witnesses", + "vote_for_witnesses": "Vote for Witnesses", + "privacy_policy": "Privacy Policy", + "terms_of_service": "Terms of Service", + "sign_up": "Join", + "learn_more": "Learn More", + "welcome": "Welcome", + "faq": "FAQ", + "shop": "The Steemit Shop", + "chat": "Steemit Chat", + "app_center": "Steemit App Center", + "api_docs": "Steemit API Docs", + "bluepaper": "Steem Bluepaper", + "whitepaper": "Steem Whitepaper", + "intro_tagline": "머니 톡스", + "intro_paragraph": + "Your voice is worth something. Join the community that pays you to post and curate high quality content." + }, + "main_menu": { + "hot": "인기글", + "trending": "추천글" + }, + "reply_editor": { + "shorten_title": "Shorten title", + "exceeds_maximum_length": "Exceeds maximum length (%(maxKb)sKB)", + "including_the_category": + " (including the category '%(rootCategory)s')", + "use_limited_amount_of_tags": + "You have %(tagsLength)s tags total%(includingCategory)s. Please use only 5 in your post and category line.", + "are_you_sure_you_want_to_clear_this_form": + "Are you sure you want to clear this form?", + "uploading": "Uploading", + "draft_saved": "Draft saved.", + "editor": "Editor", + "insert_images_by_dragging_dropping": + "이미지 추가를 위해서는 드래그&드롭, ", + "pasting_from_the_clipboard": "클립보드에서 분여넣기, ", + "selecting_them": "혹은 직접 업로드 하세요.", + "image_upload": "사진 업로드", + "power_up_100": "100%% 스팀파워로 받음", + "default_50_50": "스팀파워 50%% + 스팀달러 50%%)", + "decline_payout": "수익을 스팀 커뮤니티에 기부", + "check_this_to_auto_upvote_your_post": + "Check this to auto-upvote your post", + "markdown_styling_guide": "Markdown Styling Guide", + "or_by": "or by", + "title": "Title", + "update_post": "Update Post", + "markdown_not_supported": "Markdown is not supported here" + }, + "category_selector_jsx": { + "tag_your_story": + "최대 5개의 태그를 입력하세요. 메인 태그는 가장 먼저 입력하세요.", + "select_a_tag": "태그를 선택하세요.", + "maximum_tag_length_is_24_characters": "태그는 24 글자까지 가능합니다.", + "use_limited_amount_of_categories": + "태그는 최대 %(amount)s 개 까지 입력 가능합니다.", + "use_only_lowercase_letters": "소문자만 사용해 주세요.", + "use_one_dash": "대쉬는 한개만 사용 가능합니다.", + "use_spaces_to_separate_tags": + "태그와 태그 사이는 공백으로 구분 하세요.", + "use_only_allowed_characters": + "소문자, 숫자 그리고 대쉬만 사용 가능합니다.", + "must_start_with_a_letter": "태그는 반드시 문자로 시작해야 합니다.", + "must_end_with_a_letter_or_number": + "태그는 반드시 문자나 숫자로 끝나야 합니다." + }, + "postfull_jsx": { + "this_post_is_not_available_due_to_a_copyright_claim": + "This post is not available due to a copyright claim.", + "share_on_facebook": "Share on Facebook", + "share_on_twitter": "Share on Twitter", + "share_on_linkedin": "Share on Linkedin", + "recent_password": "Recent Password", + "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": + "In 3.5 days, convert %(amount)s %(DEBT_TOKEN)s into %(LIQUID_TOKEN)s", + "view_the_full_context": "View the full context", + "view_the_direct_parent": "View the direct parent", + "you_are_viewing_a_single_comments_thread_from": + "You are viewing a single comment's thread from" + }, + "market_jsx": { + "action": "Action", + "date_created": "Date Created", + "last_price": "Last price", + "24h_volume": "24h volume", + "spread": "Spread", + "total": "Total", + "available": "Available", + "lowest_ask": "Lowest ask", + "highest_bid": "Highest bid", + "buy_orders": "Buy Orders", + "sell_orders": "Sell Orders", + "trade_history": "Trade History", + "open_orders": "Open Orders", + "sell_amount_for_atleast": + "Sell %(amount_to_sell)s for at least %(min_to_receive)s (%(effectivePrice)s)", + "buy_atleast_amount_for": + "Buy at least %(min_to_receive)s for %(amount_to_sell)s (%(effectivePrice)s)", + "price_warning_above": + "This price is well above the current market price of %(marketPrice)s, are you sure?", + "price_warning_below": + "This price is well below the current market price of %(marketPrice)s, are you sure?", + "order_cancel_confirm": "Cancel order %(order_id)s from %(user)s?", + "order_cancelled": "Order %(order_id)s cancelled.", + "higher": "Higher", + "lower": "Lower", + "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": + "Total %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" + }, + "recoveraccountstep1_jsx": { + "begin_recovery": "Begin Recovery", + "not_valid": "Not valid", + "account_name_is_not_found": "Account name is not found", + "unable_to_recover_account_not_change_ownership_recently": + "We are unable to recover this account, it has not changed ownership recently.", + "password_not_used_in_last_days": + "This password was not used on this account in the last 30 days.", + "request_already_submitted_contact_support": + "Your request has been already submitted and we are working on it. Please contact %(SUPPORT_EMAIL)s for the status of your request.", + "recover_account_intro": + "From time to time, a Steemian's owner key may be compromised. Stolen Account Recovery gives the rightful account owner 30 days to recover their account from the moment the thief changed their owner key. Stolen Account Recovery can only be used on %(APP_URL)s if the account owner had previously listed '%(APP_NAME)s' as their account trustee and complied with %(APP_NAME)s's Terms of Service.", + "login_with_facebook_or_reddit_media_to_verify_identity": + "Please login with Facebook or Reddit to verify your identity", + "login_with_social_media_to_verify_identity": + "Please login with %(provider)s to verify your identity", + "enter_email_toverify_identity": + "We need to verify your identity. Please enter your email address below to begin the verification.", + "continue_with_email": "Continue with Email", + "thanks_for_submitting_request_for_account_recovery": + "Thanks for submitting your request for Account Recovery using %(APP_NAME)s's blockchain-based multi factor authentication. We will respond to you as quickly as possible, however, please expect there may be some delay in response due to high volume of emails. Please be prepared to verify your identity.", + "recovering_account": "Recovering account", + "recover_account": "Recover Account", + "checking_account_owner": "Checking account owner", + "sending_recovery_request": "Sending recovery request", + "cant_confirm_account_ownership": + "We can't confirm account ownership. Check your password", + "account_recovery_request_not_confirmed": + "Account recovery request is not confirmed yet, please get back later, thank you for your patience." + }, + "user_profile": { + "unknown_account": "Unknown Account", + "user_hasnt_made_any_posts_yet": + "Looks like %(name)s hasn't made any posts yet!", + "user_hasnt_started_bloggin_yet": + "Looks like %(name)s hasn't started blogging yet!", + "user_hasnt_followed_anything_yet": + "Looks like %(name)s might not be following anyone yet! If %(name)s recently added new users to follow, their personalized feed will populate once new content is available.", + "user_hasnt_had_any_replies_yet": "%(name)s hasn't had any replies yet", + "looks_like_you_havent_posted_anything_yet": + "Looks like you haven't posted anything yet.", + "create_a_post": "Create a Post", + "explore_trending_articles": "Explore Trending Articles", + "read_the_quick_start_guide": "Read The Quick Start Guide", + "browse_the_faq": "Browse The FAQ", + "followers": "팔로워", + "this_is_users_reputations_score_it_is_based_on_history_of_votes": + "This is %(name)s's reputation score.\n\nThe reputation score is based on the history of votes received by the account, and is used to hide low quality content.", + "follower_count": { + "zero": "팔로워 없음", + "one": "1명의 팔로워", + "other": "%(count)s명의 팔로워" + }, + "followed_count": { + "zero": "팔로우 없음", + "one": "1명 팔로우", + "other": "%(count)s명 팔로우" + }, + "post_count": { + "zero": "게시글 없음", + "one": "1 게시글", + "other": "%(count)s 게시글" + } + }, + "authorrewards_jsx": { + "estimated_author_rewards_last_week": "금주의 예상 저자 수익", + "author_rewards_history": "저자 수익 내역" + }, + "curationrewards_jsx": { + "estimated_curation_rewards_last_week": "금주의 큐레이션 예상 수익", + "curation_rewards_history": "큐레이션 수익 내역" + }, + "post_jsx": { + "now_showing_comments_with_low_ratings": + "Now showing comments with low ratings", + "sort_order": "Sort Order", + "comments_were_hidden_due_to_low_ratings": + "Comments were hidden due to low ratings" + }, + "voting_jsx": { + "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": + "Flagging a post can remove rewards and make this material less visible. Some common reasons to flag", + "disagreement_on_rewards": "Disagreement on rewards", + "fraud_or_plagiarism": "Fraud or Plagiarism", + "hate_speech_or_internet_trolling": "Hate Speech or Internet Trolling", + "intentional_miss_categorized_content_or_spam": + "Intentional miss-categorized content or Spam", + "pending_payout": "대기중인 보상: $%(value)s", + "payout_declined": "보상을 사양함", + "max_accepted_payout": "Max Accepted Payout $%(value)s", + "promotion_cost": "Promotion Cost $%(value)s", + "past_payouts": "Past Payouts $%(value)s", + "past_payouts_author": " - Author $%(value)s", + "past_payouts_curators": " - Curators $%(value)s", + "we_will_reset_curation_rewards_for_this_post": + "will reset your curation rewards for this post", + "removing_your_vote": "Removing your vote", + "changing_to_an_upvote": "Changing to an Up-Vote", + "changing_to_a_downvote": "Changing to a Down-Vote", + "confirm_flag": "Confirm Flag", + "and_more": "and %(count)s more", + "votes_plural": { + "one": "%(count)s 보트", + "other": "%(count)s 보트" + } + }, + "witnesses_jsx": { + "witness_thread": "witness thread", + "top_witnesses": "증인 투표", + "you_have_votes_remaining": { + "zero": "You have no votes remaining", + "one": "You have 1 vote remaining", + "other": "You have %(count)s votes remaining" + }, + "you_can_vote_for_maximum_of_witnesses": + "You can vote for a maximum of 30 witnesses", + "witness": "증인", + "information": "Information", + "if_you_want_to_vote_outside_of_top_enter_account_name": + "If you would like to vote for a witness outside of the top 50, enter the account name below to cast a vote", + "set_witness_proxy": + "You can also choose a proxy that will vote for witnesses for you. This will reset your current witness selection.", + "witness_set": + "You have set a voting proxy. If you would like to re-enable manual voting, please clear your proxy.", + "witness_proxy_current": "Your current proxy is", + "witness_proxy_set": "Set proxy", + "witness_proxy_clear": "Clear proxy", + "proxy_update_error": "Your proxy was not updated" + }, + "votesandcomments_jsx": { + "no_responses_yet_click_to_respond": + "No responses yet. Click to respond.", + "response_count_tooltip": { + "zero": "no responses. Click to respond.", + "one": "1 response. Click to respond.", + "other": "%(count)s responses. Click to respond." + }, + "vote_count": { + "zero": "추천 없음", + "one": "1개의 추천", + "other": "%(count)s개의 추천" + } + }, + "userkeys_jsx": { + "public": "Public", + "private": "Private", + "public_something_key": "Public %(key)s Key", + "private_something_key": "Private %(key)s Key", + "posting_key_is_required_it_should_be_different": + "The posting key is used for posting and voting. It should be different from the active and owner keys.", + "the_active_key_is_used_to_make_transfers_and_place_orders": + "The active key is used to make transfers and place orders in the internal market.", + "the_owner_key_is_required_to_change_other_keys": + "The owner key is the master key for the account and is required to change the other keys.", + "the_private_key_or_password_should_be_kept_offline": + "The private key or password for the owner key should be kept offline as much as possible.", + "the_memo_key_is_used_to_create_and_read_memos": + "The memo key is used to create and read memos." + }, + "suggestpassword_jsx": { + "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": + "%(APP_NAME)s cannot recover passwords. Keep this page in a secure location, such as a fireproof safe or safety deposit box.", + "APP_NAME_password_backup": "%(APP_NAME)s Password Backup", + "APP_NAME_password_backup_required": + "%(APP_NAME)s Password Backup (required)", + "after_printing_write_down_your_user_name": + "After printing, write down your user name" + }, + "converttosteem_jsx": { + "your_existing_DEBT_TOKEN_are_liquid_and_transferable": + "Your existing %(DEBT_TOKEN)s are liquid and transferable. Instead you may wish to trade %(DEBT_TOKEN)s directly in this site under %(link)s or transfer to an external market.", + "this_is_a_price_feed_conversion": + "This is a price feed conversion. The 3.5 day delay is necessary to prevent abuse from gaming the price feed average", + "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", + "DEBT_TOKEN_will_be_unavailable": + "This action will take place 3.5 days from now and can not be canceled. These %(DEBT_TOKEN)s will immediately become unavailable" + }, + "tips_js": { + "liquid_token": + "Tradeable tokens that may be transferred anywhere at anytime.
        %(LIQUID_TOKEN)s can be converted to %(VESTING_TOKEN)s in a process called powering up.", + "influence_token": + "Influence tokens which give you more control over post payouts and allow you to earn on curation rewards.", + "estimated_value": + "The estimated value is based on an average value of %(LIQUID_TOKEN)s in US dollars.", + "non_transferable": + "%(VESTING_TOKEN)s is non-transferable and requires 3 months (13 payments) to convert back to %(LIQUID_TOKEN)s.", + "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": + "Converted %(VESTING_TOKEN)s can be sent to yourself or someone else but can not transfer again without converting back to %(LIQUID_TOKEN)s.", + "part_of_your_steem_power_is_currently_delegated": + "Part of your STEEM POWER is currently delegated to you. Delegation is donated for influence or to help new users perform actions on steemit. Your delegation amount can fluctuate." + }, + "promote_post_jsx": { + "promote_post": "Promote Post", + "spend_your_DEBT_TOKEN_to_advertise_this_post": + "Spend your %(DEBT_TOKEN)s's to advertise this post in the promoted content section", + "you_successfully_promoted_this_post": + "You successfully promoted this post", + "this_post_was_hidden_due_to_low_ratings": + "This post was hidden due to low ratings" + }, + "about_jsx": { + "about_app": "About %(APP_NAME)s", + "about_app_details": + "%(APP_NAME)s is a social media platform where everyone gets paid for creating and curating content. It leverages a robust digital points system, called Steem, that supports real value for digital rewards through market price discovery and liquidity.", + "learn_more_at_app_url": "Learn more at %(APP_URL)s", + "resources": "Resources" + }, + "markdownviewer_jsx": { + "images_were_hidden_due_to_low_ratings": + "Images were hidden due to low ratings." + }, + "postsummary_jsx": { + "resteemed": "리스팀", + "resteemed_by": "리스팀 by", + "reveal_it": "Reveal this post", + "adjust_your": "adjust your", + "display_preferences": "display preferences", + "create_an_account": "create an account", + "to_save_your_preferences": "to save your preferences" + }, + "posts_index": { + "empty_feed_1": "Looks like you haven't followed anything yet", + "empty_feed_2": + "If you recently added new users to follow, your personalized feed will populate once new content is available", + "empty_feed_3": "Explore Trending Articles", + "empty_feed_4": "Read The Quick Start Guide", + "empty_feed_5": "Browse The FAQ" + }, + "transferhistoryrow_jsx": { + "to_savings": "to savings", + "from_savings": "from savings", + "cancel_transfer_from_savings": "Cancel transfer from savings", + "stop_power_down": "Stop power down", + "start_power_down_of": "Start power down of", + "receive_interest_of": "Receive interest of" + }, + "savingswithdrawhistory_jsx": { + "cancel_this_withdraw_request": "Cancel this withdraw request?", + "pending_savings_withdrawals": "PENDING SAVINGS WITHDRAWS", + "withdraw": "Withdraw %(amount)s", + "to": "to %(to)s", + "from_to": "from %(from)s to %(to)s" + }, + "explorepost_jsx": { + "copied": "Copied!", + "copy": "COPY", + "alternative_sources": "Alternative Sources" + }, + "header_jsx": { + "home": "home", + "create_a_post": "Create a post", + "change_account_password": "Change Account Password", + "create_account": "Create Account", + "stolen_account_recovery": "Stolen Account Recovery", + "people_following": "People following", + "people_followed_by": "People followed by", + "curation_rewards_by": "Curation rewards by", + "author_rewards_by": "Author rewards by", + "replies_to": "Replies to", + "comments_by": "Comments by" + }, + "loginform_jsx": { + "you_need_a_private_password_or_key": + "You need a private password or key (not a public key)", + "cryptography_test_failed": "Cryptography test failed", + "unable_to_log_you_in": + "We will be unable to log you in with this browser.", + "the_latest_versions_of": "The latest versions of ", + "are_well_tested_and_known_to_work_with": + "are well tested and known to work with %(APP_URL)s.", + "due_to_server_maintenance": + "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", + "login_to_vote": "Login to Vote", + "login_to_post": "Login to Post", + "login_to_comment": "Login to Comment", + "posting": "Posting", + "active_or_owner": "Active or Owner", + "this_password_is_bound_to_your_account_owner_key": + "This password is bound to your account's owner key and can not be used to login to this site.", + "however_you_can_use_it_to": "However, you can use it to ", + "update_your_password": "update your password", + "to_obtain_a_more_secure_set_of_keys": + "to obtain a more secure set of keys.", + "this_password_is_bound_to_your_account_active_key": + "This password is bound to your account's active key and can not be used to login to this page.", + "you_may_use_this_active_key_on_other_more": + "You may use this active key on other more secure pages like the Wallet or Market pages.", + "you_account_has_been_successfully_created": + "You account has been successfully created!", + "you_account_has_been_successfully_recovered": + "You account has been successfully recovered!", + "password_update_succes": + "The password for %(accountName)s was successfully updated", + "password_info": + "This password or private key was entered incorrectly. There is probably a handwriting or data-entry error. Hint: A password or private key generated by Steemit will never contain 0 (zero), O (capital o), I (capital i) and l (lower case L) characters.", + "enter_your_username": "Enter your username", + "password_or_wif": "Password or WIF", + "this_operation_requires_your_key_or_master_password": + "This operation requires your %(authType)s key or Master password.", + "keep_me_logged_in": "Keep me logged in", + "amazing_community": "amazing community", + "to_comment_and_reward_others": " to comment and reward others.", + "signup_button": "Sign up now to earn ", + "signup_button_emphasis": "FREE STEEM!", + "sign_up_get_steem": "Sign up. Get STEEM", + "returning_users": "Returning Users: ", + "join_our": "Join our" + }, + "chainvalidation_js": { + "account_name_should": "Account name should ", + "not_be_empty": "not be empty.", + "be_longer": "be longer.", + "be_shorter": "be shorter.", + "each_account_segment_should": "Each account segment should ", + "start_with_a_letter": "start with a letter.", + "have_only_letters_digits_or_dashes": + "have only letters, digits, or dashes.", + "have_only_one_dash_in_a_row": "have only one dash in a row.", + "end_with_a_letter_or_digit": "end with a letter or digit.", + "verified_exchange_no_memo": + "You must include a memo for your exchange transfer." + }, + "settings_jsx": { + "invalid_url": "잘못된 URL 입니다", + "name_is_too_long": "너무 긴 사용자 이름입니다", + "name_must_not_begin_with": "사용자 이름은 반드시 @로 시작해야 합니다", + "about_is_too_long": "About is too long", + "location_is_too_long": "너무 긴 지역명입니다", + "website_url_is_too_long": "너무 긴 웹사이트 URL입니다", + "public_profile_settings": "공개 프로파일 설정", + "private_post_display_settings": "Private Post Display Settings", + "not_safe_for_work_nsfw_content": "Not safe for work (NSFW) content", + "always_hide": "항상 숨기기", + "always_warn": "항상 경고하기", + "always_show": "항상 보이기", + "muted_users": "뮤트한 사용자", + "update": "저장", + "profile_image_url": "프로파일 사진 경로", + "cover_image_url": "커버이미지 사진 경로", + "profile_name": "닉네임", + "profile_about": "한줄 소개", + "profile_location": "지역", + "profile_website": "웹사이트" + }, + "transfer_jsx": { + "amount_is_in_form": "Amount is in the form 99999.999", + "insufficient_funds": "Insufficient funds", + "use_only_3_digits_of_precison": "Use only 3 digits of precison", + "send_to_account": "Send to account", + "asset": "Asset", + "this_memo_is_private": "This memo is private", + "this_memo_is_public": "This memo is public", + "convert_to_VESTING_TOKEN": "Convert to %(VESTING_TOKEN)s", + "balance_subject_to_3_day_withdraw_waiting_period": + "Balance subject to 3 day withdraw waiting period,", + "move_funds_to_another_account": + "Move funds to another %(APP_NAME)s account.", + "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": + "Protect funds by requiring a 3 day withdraw waiting period.", + "withdraw_funds_after_the_required_3_day_waiting_period": + "Withdraw funds after the required 3 day waiting period.", + "from": "From", + "to": "To", + "asset_currently_collecting": + "%(asset)s currently collecting %(interest)s%% APR.", + "beware_of_spam_and_phishing_links": + "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." + }, + "userwallet_jsx": { + "conversion_complete_tip": "Will complete on", + "in_conversion": "%(amount)s in conversion", + "transfer_to_savings": "Transfer to Savings", + "power_up": "Power Up", + "power_down": "Power Down", + "market": "Market", + "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", + "withdraw_LIQUID_TOKEN": "Withdraw %(LIQUID_TOKEN)s", + "withdraw_DEBT_TOKENS": "Withdraw %(DEBT_TOKENS)s", + "tokens_worth_about_1_of_LIQUID_TICKER": + "Tokens worth about $1.00 of %(LIQUID_TICKER)s, currently collecting %(sbdInterest)s%% APR.", + "savings": "SAVINGS", + "estimated_account_value": "Estimated Account Value", + "next_power_down_is_scheduled_to_happen": + "The next power down is scheduled to happen", + "transfers_are_temporary_disabled": "Transfers are temporary disabled.", + "history": "HISTORY", + "redeem_rewards": "내 지갑으로 입금하기", + "buy_steem_or_steem_power": "STEEM, STEEM POWER 구입" + }, + "powerdown_jsx": { + "power_down": "파워다운", + "amount": "Amount", + "already_power_down": + "You are already powering down %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s paid out so far). Note that if you change the power down amount the payout schedule will reset.", + "delegating": + "You are delegating %(AMOUNT)s %(LIQUID_TICKER)s. That amount is locked up and not available to power down until the delegation is removed and a full reward period has passed.", + "per_week": "That's ~%(AMOUNT)s %(LIQUID_TICKER)s per week.", + "warning": + "Leaving less than %(AMOUNT)s %(VESTING_TOKEN)s in your account is not recommended and can leave your account in a unusable state.", + "error": "Unable to power down (ERROR: %(MESSAGE)s)" + }, + "checkloginowner_jsx": { + "your_password_permissions_were_reduced": + "Your password permissions were reduced", + "if_you_did_not_make_this_change": + "If you did not make this change please", + "ownership_changed_on": "Ownership Changed On ", + "deadline_for_recovery_is": "Deadline for recovery is", + "i_understand_dont_show_again": "이 메시지를 다시 보지 않기" } - }, - "userkeys_jsx": { - "public": "Public", - "private": "Private", - "public_something_key": "Public %(key)s Key", - "private_something_key": "Private %(key)s Key", - "posting_key_is_required_it_should_be_different": "The posting key is used for posting and voting. It should be different from the active and owner keys.", - "the_active_key_is_used_to_make_transfers_and_place_orders": "The active key is used to make transfers and place orders in the internal market.", - "the_owner_key_is_required_to_change_other_keys": "The owner key is the master key for the account and is required to change the other keys.", - "the_private_key_or_password_should_be_kept_offline": "The private key or password for the owner key should be kept offline as much as possible.", - "the_memo_key_is_used_to_create_and_read_memos": "The memo key is used to create and read memos." - }, - "suggestpassword_jsx": { - "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": "%(APP_NAME)s cannot recover passwords. Keep this page in a secure location, such as a fireproof safe or safety deposit box.", - "APP_NAME_password_backup": "%(APP_NAME)s Password Backup", - "APP_NAME_password_backup_required": "%(APP_NAME)s Password Backup (required)", - "after_printing_write_down_your_user_name": "After printing, write down your user name" - }, - "converttosteem_jsx": { - "your_existing_DEBT_TOKEN_are_liquid_and_transferable": "Your existing %(DEBT_TOKEN)s are liquid and transferable. Instead you may wish to trade %(DEBT_TOKEN)s directly in this site under %(link)s or transfer to an external market.", - "this_is_a_price_feed_conversion": "This is a price feed conversion. The 3.5 day delay is necessary to prevent abuse from gaming the price feed average", - "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", - "DEBT_TOKEN_will_be_unavailable": "This action will take place 3.5 days from now and can not be canceled. These %(DEBT_TOKEN)s will immediately become unavailable" - }, - "tips_js": { - "liquid_token": "Tradeable tokens that may be transferred anywhere at anytime.
        %(LIQUID_TOKEN)s can be converted to %(VESTING_TOKEN)s in a process called powering up.", - "influence_token": "Influence tokens which give you more control over post payouts and allow you to earn on curation rewards.", - "estimated_value": "The estimated value is based on an average value of %(LIQUID_TOKEN)s in US dollars.", - "non_transferable": "%(VESTING_TOKEN)s is non-transferable and requires 3 months (13 payments) to convert back to %(LIQUID_TOKEN)s.", - "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": "Converted %(VESTING_TOKEN)s can be sent to yourself or someone else but can not transfer again without converting back to %(LIQUID_TOKEN)s.", - "part_of_your_steem_power_is_currently_delegated": "Part of your STEEM POWER is currently delegated to you. Delegation is donated for influence or to help new users perform actions on steemit. Your delegation amount can fluctuate." - }, - "promote_post_jsx": { - "promote_post": "Promote Post", - "spend_your_DEBT_TOKEN_to_advertise_this_post": "Spend your %(DEBT_TOKEN)s's to advertise this post in the promoted content section", - "you_successfully_promoted_this_post": "You successfully promoted this post", - "this_post_was_hidden_due_to_low_ratings": "This post was hidden due to low ratings" - }, - "about_jsx": { - "about_app": "About %(APP_NAME)s", - "about_app_details": "%(APP_NAME)s is a social media platform where everyone gets paid for creating and curating content. It leverages a robust digital points system, called Steem, that supports real value for digital rewards through market price discovery and liquidity.", - "learn_more_at_app_url": "Learn more at %(APP_URL)s", - "resources": "Resources" - }, - "markdownviewer_jsx": { - "images_were_hidden_due_to_low_ratings": "Images were hidden due to low ratings." - }, - "postsummary_jsx": { - "resteemed": "리스팀", - "resteemed_by": "리스팀 by", - "reveal_it": "Reveal this post", - "adjust_your": "adjust your", - "display_preferences": "display preferences", - "create_an_account": "create an account", - "to_save_your_preferences": "to save your preferences" - }, - "posts_index": { - "empty_feed_1": "Looks like you haven't followed anything yet", - "empty_feed_2": "If you recently added new users to follow, your personalized feed will populate once new content is available", - "empty_feed_3": "Explore Trending Articles", - "empty_feed_4": "Read The Quick Start Guide", - "empty_feed_5": "Browse The FAQ" - }, - "transferhistoryrow_jsx": { - "to_savings": "to savings", - "from_savings": "from savings", - "cancel_transfer_from_savings": "Cancel transfer from savings", - "stop_power_down": "Stop power down", - "start_power_down_of": "Start power down of", - "receive_interest_of": "Receive interest of" - }, - "savingswithdrawhistory_jsx": { - "cancel_this_withdraw_request": "Cancel this withdraw request?", - "pending_savings_withdrawals": "PENDING SAVINGS WITHDRAWS", - "withdraw": "Withdraw %(amount)s", - "to": "to %(to)s", - "from_to": "from %(from)s to %(to)s" - }, - "explorepost_jsx": { - "copied": "Copied!", - "copy": "COPY", - "alternative_sources": "Alternative Sources" - }, - "header_jsx": { - "home": "home", - "create_a_post": "Create a post", - "change_account_password": "Change Account Password", - "create_account": "Create Account", - "stolen_account_recovery": "Stolen Account Recovery", - "people_following": "People following", - "people_followed_by": "People followed by", - "curation_rewards_by": "Curation rewards by", - "author_rewards_by": "Author rewards by", - "replies_to": "Replies to", - "comments_by": "Comments by" - }, - "loginform_jsx": { - "you_need_a_private_password_or_key": "You need a private password or key (not a public key)", - "cryptography_test_failed": "Cryptography test failed", - "unable_to_log_you_in": "We will be unable to log you in with this browser.", - "the_latest_versions_of": "The latest versions of ", - "are_well_tested_and_known_to_work_with": "are well tested and known to work with %(APP_URL)s.", - "due_to_server_maintenance": "Due to server maintenance we are running in read only mode. We are sorry for the inconvenience.", - "login_to_vote": "Login to Vote", - "login_to_post": "Login to Post", - "login_to_comment": "Login to Comment", - "posting": "Posting", - "active_or_owner": "Active or Owner", - "this_password_is_bound_to_your_account_owner_key": "This password is bound to your account's owner key and can not be used to login to this site.", - "however_you_can_use_it_to": "However, you can use it to ", - "update_your_password": "update your password", - "to_obtain_a_more_secure_set_of_keys": "to obtain a more secure set of keys.", - "this_password_is_bound_to_your_account_active_key": "This password is bound to your account's active key and can not be used to login to this page.", - "you_may_use_this_active_key_on_other_more": "You may use this active key on other more secure pages like the Wallet or Market pages.", - "you_account_has_been_successfully_created": "You account has been successfully created!", - "you_account_has_been_successfully_recovered": "You account has been successfully recovered!", - "password_update_succes": "The password for %(accountName)s was successfully updated", - "password_info": "This password or private key was entered incorrectly. There is probably a handwriting or data-entry error. Hint: A password or private key generated by Steemit will never contain 0 (zero), O (capital o), I (capital i) and l (lower case L) characters.", - "enter_your_username": "Enter your username", - "password_or_wif": "Password or WIF", - "this_operation_requires_your_key_or_master_password": "This operation requires your %(authType)s key or Master password.", - "keep_me_logged_in": "Keep me logged in", - "amazing_community": "amazing community", - "to_comment_and_reward_others": " to comment and reward others.", - "signup_button": "Sign up now to earn ", - "signup_button_emphasis": "FREE STEEM!", - "sign_up_get_steem": "Sign up. Get STEEM", - "returning_users": "Returning Users: ", - "join_our": "Join our" - }, - "chainvalidation_js": { - "account_name_should": "Account name should ", - "not_be_empty": "not be empty.", - "be_longer": "be longer.", - "be_shorter": "be shorter.", - "each_account_segment_should": "Each account segment should ", - "start_with_a_letter": "start with a letter.", - "have_only_letters_digits_or_dashes": "have only letters, digits, or dashes.", - "have_only_one_dash_in_a_row": "have only one dash in a row.", - "end_with_a_letter_or_digit": "end with a letter or digit.", - "verified_exchange_no_memo": "You must include a memo for your exchange transfer." - }, - "settings_jsx": { - "invalid_url": "잘못된 URL 입니다", - "name_is_too_long": "너무 긴 사용자 이름입니다", - "name_must_not_begin_with": "사용자 이름은 반드시 @로 시작해야 합니다", - "about_is_too_long": "About is too long", - "location_is_too_long": "너무 긴 지역명입니다", - "website_url_is_too_long": "너무 긴 웹사이트 URL입니다", - "public_profile_settings": "공개 프로파일 설정", - "private_post_display_settings": "Private Post Display Settings", - "not_safe_for_work_nsfw_content": "Not safe for work (NSFW) content", - "always_hide": "항상 숨기기", - "always_warn": "항상 경고하기", - "always_show": "항상 보이기", - "muted_users": "뮤트한 사용자", - "update": "저장", - "profile_image_url": "프로파일 사진 경로", - "cover_image_url": "커버이미지 사진 경로", - "profile_name": "닉네임", - "profile_about": "한줄 소개", - "profile_location": "지역", - "profile_website": "웹사이트" - }, - "transfer_jsx": { - "amount_is_in_form": "Amount is in the form 99999.999", - "insufficient_funds": "Insufficient funds", - "use_only_3_digits_of_precison": "Use only 3 digits of precison", - "send_to_account": "Send to account", - "asset": "Asset", - "this_memo_is_private": "This memo is private", - "this_memo_is_public": "This memo is public", - "convert_to_VESTING_TOKEN": "Convert to %(VESTING_TOKEN)s", - "balance_subject_to_3_day_withdraw_waiting_period": "Balance subject to 3 day withdraw waiting period,", - "move_funds_to_another_account": "Move funds to another %(APP_NAME)s account.", - "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": "Protect funds by requiring a 3 day withdraw waiting period.", - "withdraw_funds_after_the_required_3_day_waiting_period": "Withdraw funds after the required 3 day waiting period.", - "from": "From", - "to": "To", - "asset_currently_collecting": "%(asset)s currently collecting %(interest)s%% APR.", - "beware_of_spam_and_phishing_links": "Beware of spam and phishing links in transfer memos. Do not open links from users you do not trust. Do not provide your private keys to any third party websites." - }, - "userwallet_jsx": { - "conversion_complete_tip": "Will complete on", - "in_conversion": "%(amount)s in conversion", - "transfer_to_savings": "Transfer to Savings", - "power_up": "Power Up", - "power_down": "Power Down", - "market": "Market", - "convert_to_LIQUID_TOKEN": "Convert to %(LIQUID_TOKEN)s", - "withdraw_LIQUID_TOKEN": "Withdraw %(LIQUID_TOKEN)s", - "withdraw_DEBT_TOKENS": "Withdraw %(DEBT_TOKENS)s", - "tokens_worth_about_1_of_LIQUID_TICKER": "Tokens worth about $1.00 of %(LIQUID_TICKER)s, currently collecting %(sbdInterest)s%% APR.", - "savings": "SAVINGS", - "estimated_account_value": "Estimated Account Value", - "next_power_down_is_scheduled_to_happen": "The next power down is scheduled to happen", - "transfers_are_temporary_disabled": "Transfers are temporary disabled.", - "history": "HISTORY", - "redeem_rewards": "내 지갑으로 입금하기", - "buy_steem_or_steem_power": "STEEM, STEEM POWER 구입" - }, - "powerdown_jsx": { - "power_down": "파워다운", - "amount": "Amount", - "already_power_down": "You are already powering down %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s paid out so far). Note that if you change the power down amount the payout schedule will reset.", - "delegating": "You are delegating %(AMOUNT)s %(LIQUID_TICKER)s. That amount is locked up and not available to power down until the delegation is removed and a full reward period has passed.", - "per_week": "That's ~%(AMOUNT)s %(LIQUID_TICKER)s per week.", - "warning": "Leaving less than %(AMOUNT)s %(VESTING_TOKEN)s in your account is not recommended and can leave your account in a unusable state.", - "error": "Unable to power down (ERROR: %(MESSAGE)s)" - }, - "checkloginowner_jsx": { - "your_password_permissions_were_reduced": "Your password permissions were reduced", - "if_you_did_not_make_this_change": "If you did not make this change please", - "ownership_changed_on": "Ownership Changed On ", - "deadline_for_recovery_is": "Deadline for recovery is", - "i_understand_dont_show_again": "이 메시지를 다시 보지 않기" - } } diff --git a/src/app/locales/ru.json b/src/app/locales/ru.json index ddf516859..19a90fdb0 100644 --- a/src/app/locales/ru.json +++ b/src/app/locales/ru.json @@ -1,669 +1,799 @@ { - "g": { - "age": "возраст", - "amount": "Количество", - "and": "и", - "are_you_sure": "Вы уверены?", - "ask": "Продажа", - "balance": "Баланс", - "balances": "Балансы", - "bid": "Покупка", - "blog": "Блог", - "browse": "Посмотреть", - "buy": "Купить", - "buy_or_sell": "Купить или Продать", - "by": "от", - "cancel": "Отмена", - "change_password": "Сменить пароль", - "choose_language": "Выберите язык", - "clear": "Очистить", - "close": "Закрыть", - "collapse_or_expand": "Свернуть/Развернуть", - "comments": "Комментарии", - "confirm": "Подтвердить", - "convert": "Конвертировать", - "date": "Дата", - "delete": "Удалить", - "dismiss": "Скрыть", - "edit": "Редактировать", - "email": "Электронная почта", - "feed": "Лента", - "follow": "Подписаться", - "for": " для ", - "from": " от ", - "go_back": "Назад", - "hide": "Скрыть", - "in": "в", - "in_reply_to": "в ответ на", - "insufficient_balance": "Недостаточный баланс", - "invalid_amount": "Недостаточно средств", - "joined": "Присоединился", - "loading": "Загрузка", - "login": "Войти", - "logout": "Выйти", - "memo": "Примечание", - "mute": "Заблокировать", - "new": "новое", - "newer": "Новее", - "next": "Следующий", - "no": "Нет", - "ok": "ОК", - "older": "Старее", - "or": "или", - "order_placed": "Заказ размещен", - "password": "Пароль", - "payouts": "Выплаты", - "permissions": "Разрешения", - "phishy_message": "Link expanded to plain text; beware of a potential phishing attempt", - "post": "Пост", - "post_as": "Запостить как", - "posts": "Посты", - "powered_up_100": "100%% в Силе Голоса", - "preview": "Предварительный просмотр", - "previous": "Предыдущий", - "price": "Цена", - "print": "Распечатать", - "promote": "Продвинуть", - "promoted": "продвигаемое", - "re": "RE", - "re_to": "RE: %(topic)s", - "recent_password": "Недавний пароль", - "receive": "Получено ", - "remove": "Удалить", - "remove_vote": "Убрать голос", - "replied_to": "ответил %(account)s", - "replies": "Ответы", - "reply": "Ответить", - "reply_count": { - "zero": "нет ответов", - "one": "1 ответ", - "few": "%(count)s ответа", - "many": "%(count)s ответов", - "other": "%(count)s ответов" - }, - "reputation": "Репутация", - "reveal_comment": "Показать комментарий", - "request": "запрос", - "required": "Обязательно", - "rewards": "вознаграждение", - "save": "Сохранить", - "saved": "Сохранено", - "search": "Поиск", - "sell": "Продать", - "settings": "Настройки", - "share_this_post": "Поделиться этим постом", - "show": "Показать", - "sign_in": "Войти", - "sign_up": "Регистрация", - "since": "начиная с", - "submit": "Отправить", - "power_up": "Усилить силу голоса", - "submit_a_story": "Добавить пост", - "tag": "Тег", - "to": " к ", - "all_tags": "All tags", - "transfer": "Перевод ", - "trending_topics": "Популярное", - "type": "Тип", - "unfollow": "Отписаться", - "unmute": "Разблокировать", - "unknown": "Неизвестно", - "upvote": "Голосовать за", - "upvote_post": "Проголосовать за пост", - "username": "Имя пользователя", - "version": "Версия", - "vote": "Проголосовать", - "votes": "голосов", - "wallet": "Кошелек", - "warning": "внимание", - "yes": "Да", - "posting": "Постинг", - "owner": "Владелец", - "active": "Активный", - "account_not_found": "Аккаунт не найден", - "this_is_wrong_password": "Это неправильный пароль", - "do_you_need_to": "Вам нужно", - "account_name": "Имя аккаунта", - "recover_your_account": "восстановить ваш аккаунт", - "reset_usernames_password": "Сбросить пароль пользователя %(username)s", - "this_will_update_usernames_authtype_key": "Это обновит %(username)s %(authType)s ключ", - "passwords_do_not_match": "Пароли не совпадают", - "you_need_private_password_or_key_not_a_public_key": "Вам нужен приватный пароль или ключ (не публичный ключ)", - "the_rules_of_APP_NAME": { - "one": "Первое правило сети %(APP_NAME)s: не теряйте свой пароль.", - "second": "Второе правило %(APP_NAME)s: Не теряйте свой пароль.", - "third": "Третье правило %(APP_NAME)s: мы не можем восстановить ваш пароль.", - "fourth": "Четвертое правило: если вы можете запомнить свой пароль, значит он не безопасен.", - "fifth": "Пятое правило: используйте только сгенерированные случайным образом пароли.", - "sixth": "Шестое правило: Никому не говорите свой пароль.", - "seventh": "Седьмое правило: Всегда надежно храните свой пароль." - }, - "recover_password": "Восстановить аккаунт", - "current_password": "Текущий пароль", - "generated_password": "Сгенерированный пароль", - "backup_password_by_storing_it": "Сделайте резервную копию в менеджере паролей или текстовом файле", - "enter_account_show_password": "Введите действительное имя аккаунта, чтобы показать пароль", - "click_to_generate_password": "Нажмите, чтобы сгененировать пароль", - "re_enter_generate_password": "Повторно введите пароль", - "understand_that_APP_NAME_cannot_recover_password": "Я понимаю, что %(APP_NAME)s не может восстановить потерянные пароли", - "i_saved_password": "Я надежно сохранил сгенерированный пароль", - "update_password": "Обновить пароль", - "confirm_password": "Подтвердить пароль", - "account_updated": "Аккаунт обновлен", - "password_must_be_characters_or_more": "Пароль должен содержать %(amount)s символ(а) или больше", - "need_password_or_key": "Вам нужен приватный пароль или ключ (не публичный ключ)", - "login_to_see_memo": "войти чтобы увидеть примечание", - "new_password": "Новый пароль", - "incorrect_password": "Неправильный пароль", - "username_does_not_exist": "Имя пользователя не найдено", - "account_name_should_start_with_a_letter": "Имя аккаунта должно начинаться с буквы.", - "account_name_should_be_shorter": "Имя аккаунта должно быть короче.", - "account_name_should_be_longer": "Имя аккаунта должно быть длиннее.", - "account_name_should_have_only_letters_digits_or_dashes": "Имя аккаунта должно должно состоять только из букв, цифр или дефисов.", - "cannot_increase_reward_of_post_within_the_last_minute_before_payout": "Не удается увеличить вознаграждение за пост в последнюю минуту перед выплатой", - "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": "голос уже существует, пользователь должен обозначить желание убрать делегата", - "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": "Только один Steem аккаунт разрешен с одного IP адреса каждые десять минут", - "resteem_this_post": "Поделиться этим постом", - "reblog": "Поделиться", - "write_your_story": "Написать свою историю", - "remember_voting_and_posting_key": "Запомнить голос и постинг ключ", - "auto_login_question_mark": "Заходить автоматически?", - "hide_private_key": "Скрыть приватный ключ", - "show_private_key": "Показать приватный ключ", - "login_to_show": "Войти, чтобы показать", - "not_valid_email": "Не действительный адрес", - "thank_you_for_being_an_early_visitor_to_APP_NAME": "Благодарим вас за то что являетесь ранним посетителем %(APP_NAME)s. Мы свяжемся с Вами при первой же возможности.", - "author_rewards": "Авторские вознаграждения", - "curation_rewards": "Кураторские вознаграждения", - "sorry_your_reddit_account_doesnt_have_enough_karma": "Извините, у вашего Reddit аккаунта недостаточно Reddit кармы чтобы иметь возможность бесплатной регистрации. Пожалуйста, добавьте вашу электронную почту чтобы записаться в лист ожидания", - "register_with_facebook": "Регистрация с Facebook", - "or_click_the_button_below_to_register_with_facebook": "Или нажмите на кнопку ниже, чтобы зарегистрироваться в Facebook", - "server_returned_error": "ошибка сервера", - "APP_NAME_support": "%(APP_NAME)s поддержка", - "please_email_questions_to": "Пожалуйста, шлите ваши вопросы на электронную почту", - "next_7_strings_single_block": { - "authors_get_paid_when_people_like_you_upvote_their_post": "Авторы получают вознаграждение, когда пользователи голосуют за их посты", - "if_you_enjoyed_what_you_read_earn_amount": "Если Вам понравилось то, что Вы здесь прочитали, создайте свой аккаунт сегодня и начните зарабатывать БЕСПЛАТНЫЕ STEEM-ы!", - "free_steem": "БЕСПЛАТНЫЕ STEEM!", - "sign_up_earn_steem": "Зарегистрируйтесь сейчас, чтобы заработать " - }, - "next_3_strings_together": { - "show_more": "Показать больше", - "show_less": "Показать меньше", - "value_posts": "меньше сообщений низкой стоимости" - }, - "read_only_mode": "По техническим причинам вебсайт доступен только для чтения, приносим свои извинения.", - "tags_and_topics": "Теги и темы", - "show_more_topics": "Показать больше тем", - "basic": "Базовый", - "advanced": "Подробнее", - "views": { - "zero": "Нет просмотров", - "one": "%(count)s просмотр", - "few": "%(count)s просмотра", - "many": "%(count)s просмотров", - "other": "%(count)s просмотров" - }, - "responses": { - "zero": "Нет ответов", - "one": "%(count)s ответ", - "few": "%(count)s ответа", - "many": "%(count)s ответов", - "other": "%(count)s ответов" - }, - "post_key_warning": { - "confirm": "Вы собираетесь опубликовать ваш приватный ключ или мастер-пароль, это может привести к потере вашего акканта и всех средств на нем.", - "warning": "Если кто-то попросил вас опубликовать или показать ваш приватный ключ, это возможно злоумышленник, который пытается украсть ваши токены.", - "checkbox": "Я понял" - } - }, - "navigation": { - "about": "О проекте", - "explore": "Исследовать", - "APP_NAME_whitepaper": "Белая книга %(APP_NAME)s", - "buy_LIQUID_TOKEN": "Купить %(LIQUID_TOKEN)s", - "sell_LIQUID_TOKEN": "Продать %(LIQUID_TOKEN)s", - "currency_market": "Биржа", - "stolen_account_recovery": "Восстановление украденного аккаунта", - "change_account_password": "Изменить пароль аккаунта", - "witnesses": "Делегаты", - "vote_for_witnesses": "Проголосовать за делегатов", - "privacy_policy": "Политика Конфиденциальности", - "terms_of_service": "Условия пользования", - "sign_up": "Присоединиться", - "learn_more": "Документация", - "welcome": "Добро пожаловать", - "faq": "ЧаВО", - "shop": "Магазин Steemit", - "chat": "Чат Steemit", - "app_center": "Центр приложений Steemit", - "api_docs": "API-документация Steemit", - "bluepaper": "Синяя бумага Steem", - "smt_whitepaper": "Белая книга SMT", - "whitepaper": "Белая книга Steem", - "intro_tagline": "Здесь говорят деньги.", - "intro_paragraph": "Ваше мнение имеет цену. Присоединяйтесь к сообществу, которое платит за контент и за работу по отбору самого лучшего контента." - }, - "main_menu": { - "hot": "актуальное", - "trending": "популярное" - }, - "reply_editor": { - "shorten_title": "Сократите заголовок", - "exceeds_maximum_length": "Превышает максимальную длину (%(maxKb)sKB)", - "including_the_category": "(включая категорию «%(rootCategory)s»)", - "use_limited_amount_of_tags": "У вас %(tagsLength)s тегов, включая %(includingCategory)s. Пожалуйста, используйте не более 5 в посте и категории.", - "are_you_sure_you_want_to_clear_this_form": "Вы уверены, что вы хотите очистить эту форму?", - "uploading": "Загрузка", - "draft_saved": "Черновик сохранен.", - "editor": "Редактор", - "insert_images_by_dragging_dropping": "Вставьте изображения, перетащив их, ", - "pasting_from_the_clipboard": "или вставив из буфера обмена, ", - "selecting_them": "выбрав их", - "image_upload": "Загрузка изображения", - "power_up_100": "Сила Голоса 100%%", - "default_50_50": "По умолчанию (50%% / 50%%)", - "decline_payout": "Отказаться от выплаты", - "check_this_to_auto_upvote_your_post": "Отметьте, чтобы проголосовать за свой пост", - "markdown_styling_guide": "Руководство стилизации в Markdown", - "or_by": "или", - "title": "Заголовок", - "update_post": "Update Post", - "markdown_not_supported": "Markdown здесь не поддерживается" - }, - "category_selector_jsx": { - "tag_your_story": "Добавь теги (до 5 штук), первый тег станет основной категорией.", - "select_a_tag": "Выбрать тег", - "maximum_tag_length_is_24_characters": "Максимальная длина категории 24 знака", - "use_limited_amount_of_categories": "Пожалуйста, используйте только %(amount)s категории", - "use_only_lowercase_letters": "Используйте только символы нижнего регистра", - "use_one_dash": "Используйте только одно тире", - "use_spaces_to_separate_tags": "Используйте пробел чтобы разделить теги", - "use_only_allowed_characters": "Используйте только строчные буквы, цифры и одно тире", - "must_start_with_a_letter": "Должно начинаться с буквы", - "must_end_with_a_letter_or_number": "Должно заканчиваться с буквы или номера" - }, - "postfull_jsx": { - "this_post_is_not_available_due_to_a_copyright_claim": "Этот пост недоступен из-за жалобы о нарушении авторских прав.", - "share_on_facebook": "Поделитесь в Facebook", - "share_on_twitter": "Поделиться в Twitter", - "share_on_linkedin": "Поделиться в Linkedin", - "recent_password": "Недавний пароль", - "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": "В 3,5 дня перевести %(amount)s %(DEBT_TOKEN)s в %(LIQUID_TOKEN)s", - "view_the_full_context": "Показать полный контекст", - "view_the_direct_parent": "Просмотр прямого родителя", - "you_are_viewing_a_single_comments_thread_from": "Вы читаете одну нить комментариев от" - }, - "market_jsx": { - "action": "Действие", - "date_created": "Дата создания", - "last_price": "Последняя цена", - "24h_volume": "Объем за 24 часа", - "spread": "Спред", - "total": "Итого", - "available": "Доступно", - "lowest_ask": "Лучшая цена продажи", - "highest_bid": "Лучшая цена покупки", - "buy_orders": "Заказы на покупку", - "sell_orders": "Заказы на продажу", - "trade_history": "История сделок", - "open_orders": "Открытые сделки", - "sell_amount_for_atleast": "Продать %(amount_to_sell)s за %(min_to_receive)s по цене (%(effectivePrice)s)", - "buy_atleast_amount_for": "Купить по крайней мере %(min_to_receive)s за %(amount_to_sell)s (%(effectivePrice)s)", - "price_warning_above": "Эта цена намного выше текущей рыночной цены %(marketPrice)s, вы уверены?", - "price_warning_below": "Эта цена намного выше текущей рыночной цены %(marketPrice)s, вы уверены?", - "order_cancel_confirm": "Отменить заказ %(order_id)s от %(user)s?", - "order_cancelled": "Заказ %(order_id)s отменен.", - "higher": "Дороже", - "lower": "Дешевле", - "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": "Сумма %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" - }, - "recoveraccountstep1_jsx": { - "begin_recovery": "Начать восстановление", - "not_valid": "Недействительно", - "account_name_is_not_found": "Имя аккаунта не найдено", - "unable_to_recover_account_not_change_ownership_recently": "Мы не можем восстановить эту учетную запись, она не изменила владельца в последнее время.", - "password_not_used_in_last_days": "Этот пароль не использовался в этой учетной записи за последние 30 дней.", - "request_already_submitted_contact_support": "Ваш запрос был отправлен, и мы работаем над этим. Пожалуйста, свяжитесь с %(SUPPORT_EMAIL)s для получения статуса вашего запроса.", - "recover_account_intro": "Иногда бывает что ключ владельца может быть скомпрометирован. Восстановление украденного аккаунта дает законному владельцу 30 дней чтобы вернуть аккаунт с момента изменения владельческого ключа мошенником. Восстановление украденного аккаунта в %(APP_URL)s возможно только если владелец аккаунта ранее указал %(APP_NAME)s's в качестве доверенного лица и согласился с Условиями Использования сайта %(APP_NAME)s's.", - "login_with_facebook_or_reddit_media_to_verify_identity": "Пожалуйста, войдите используя Facebook или Reddit для подтверждения вашей личности", - "login_with_social_media_to_verify_identity": "Пожалуйста, войдите используя %(provider)s для подтверждения вашей личности", - "enter_email_toverify_identity": "Нам нужно подтвердить вашу личность. Пожалуйста, укажите вашу электронную почту ниже для начала проверки.", - "continue_with_email": "Продолжить с электронной почтой", - "thanks_for_submitting_request_for_account_recovery": "

        Благодарим Вас за отправку запроса на восстановление аккаунта используя основанную на блокчейне мультифакторную аутентификацию %(APP_NAME)s’a.

        Мы ответим Вам как можно быстрее, однако, пожалуйста, ожидайте что может быть некоторая задержка из-за большого объема писем.

        Пожалуйста, будьте готовы подтвердить свою личность.

        С уважением,

        Ned Scott

        CEO Steemit

        ", - "recovering_account": "Восстанавливаем аккаунт", - "recover_account": "Восстановить аккаунт", - "checking_account_owner": "Проверяем владельца аккаунта", - "sending_recovery_request": "Отправляем запрос восстановления", - "cant_confirm_account_ownership": "Мы не можем подтвердить владение аккаунтом. Проверьте ваш пароль", - "account_recovery_request_not_confirmed": "Запрос восстановления аккаунта еще не подтвержден, пожалуйста проверьте позднее. Спасибо за ваше терпение." - }, - "user_profile": { - "unknown_account": "Неизвестный аккаунт", - "user_hasnt_made_any_posts_yet": "Похоже, что %(name)s еще не написал постов!", - "user_hasnt_started_bloggin_yet": "Похоже. что %(name)s еще не завёл блог!", - "user_hasnt_followed_anything_yet": "Похоже, что %(name)s еще ни на кого не подписан! Если, %(name)s недавно подписался на новых пользователей, их персонализированный канал будет заполняться сразу после появления нового контента.", - "user_hasnt_had_any_replies_yet": "%(name)s еще не получил ответов", - "looks_like_you_havent_posted_anything_yet": "Похоже, ты еще ничего не опубликовал.", - "create_a_post": "Создать сообщение", - "explore_trending_articles": "Посмотреть статьи, набирающие популярность", - "read_the_quick_start_guide": "Прочтите краткое руководство", - "browse_the_faq": "Просмотреть ЧаВО", - "followers": "Подписчики", - "this_is_users_reputations_score_it_is_based_on_history_of_votes": "Это репутация %(name)s.\n\nРепутация рассчитывается на основе истории полученных голосов и используется для сокрытия низкокачественного контента.", - "follower_count": { - "zero": "Нет подписчиков", - "one": "1 подписчик", - "few": "%(count)s подписчика", - "many": "%(count)s подписчиков", - "other": "%(count)s подписчиков" - }, - "followed_count": { - "zero": "Ни на кого не подписан", - "one": "1 подписок", - "few": "%(count)s подписки", - "many": "%(count)s подписок", - "other": "%(count)s подписок" - }, - "post_count": { - "zero": "Постов нет", - "one": "1 пост", - "few": "%(count)s поста", - "many": "%(count)s постов", - "other": "%(count)s постов" - } - }, - "authorrewards_jsx": { - "estimated_author_rewards_last_week": "Оценочные авторские вознаграждения за прошлую неделю", - "author_rewards_history": "История авторских наград" - }, - "curationrewards_jsx": { - "estimated_curation_rewards_last_week": "Оценочные кураторские вознаграждения за последнюю неделю", - "curation_rewards_history": "История кураторских наград" - }, - "post_jsx": { - "now_showing_comments_with_low_ratings": "Отображаем комментарии с низким рейтингом", - "sort_order": "Порядок сортировки", - "comments_were_hidden_due_to_low_ratings": "Комментарии были скрыты из-за низкого рейтинга" - }, - "voting_jsx": { - "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": "Голосование против уменьшает вознаграждение и снижает позицию в рейтинге.", - "disagreement_on_rewards": "Несогласие с вознаграждением", - "fraud_or_plagiarism": "Мошенничество или плагиат", - "hate_speech_or_internet_trolling": "Разжигание ненависти или Интернет троллинг", - "intentional_miss_categorized_content_or_spam": "Преднамеренная неправильная категоризация контента или спам", - "pending_payout": "Ожидаемая выплата", - "payout_declined": "Автор отказался от вознаграждения", - "max_accepted_payout": "Максимально допустимое вознаграждение: $%(value)s", - "promotion_cost": "Цена продвижения: $%(value)s", - "past_payouts": "Предыдущие выплаты $%(value)s", - "past_payouts_author": " - Авторские $%(value)s", - "past_payouts_curators": " - Кураторские $%(value)s", - "we_will_reset_curation_rewards_for_this_post": "это сбросит выплаты за курирование", - "removing_your_vote": "Удаление голоса", - "changing_to_an_upvote": "Изменить на голосование 'за'", - "changing_to_a_downvote": "Изменить на голосование 'против'", - "confirm_flag": "Подтвердить голос 'против'", - "and_more": "и %(count)s больше", - "votes_plural": { - "one": "%(count)s голоса", - "few": "%(count)s голоса", - "many": "%(count)s голосов", - "other": "%(count)s голосов" - } - }, - "witnesses_jsx": { - "witness_thread": "пост делегата", - "top_witnesses": "Топ делегатов", - "you_have_votes_remaining": { - "zero": "У Вас не осталось голосов", - "one": "У Вас остался 1 голос", - "few": "У Вас осталось %(count)s голоса", - "many": "У Вас осталось %(count)s голосов", - "other": "У Вас осталось %(count)s голосов" - }, - "you_can_vote_for_maximum_of_witnesses": "Вы можете голосовать максимум за 30 делегатов", - "witness": "Делегаты", - "information": "Информация", - "if_you_want_to_vote_outside_of_top_enter_account_name": "Если вы хотите проголосовать за делегата вне top 50, введите имя аккаунта ниже", - "set_witness_proxy": "Вы также можете выбрать прокси, который будет вместо вас голосовать за делегатов. Это сбросит ваш текущий выбор делегата.", - "witness_set": "Вы установили прокси для голосования. Если вы хотите голосовать вручную, пожалуйста, очистите ваш прокси.", - "witness_proxy_current": "Ваш текущий прокси", - "witness_proxy_set": "Установить прокси", - "witness_proxy_clear": "Очистить прокси", - "proxy_update_error": "Ваш прокси не был обновлен" - }, - "votesandcomments_jsx": { - "no_responses_yet_click_to_respond": "Ответов пока нет. Нажмите чтобы ответить.", - "response_count_tooltip": { - "zero": "ответов пока нет. Нажмите чтобы ответить.", - "one": "1 ответ. Нажмите чтобы ответить.", - "few": "%(count)s ответа. Нажмите чтобы ответить.", - "many": "%(count)s ответов. Нажмите чтобы ответить.", - "other": "%(count)s ответов. Нажмите чтобы ответить." - }, - "vote_count": { - "zero": "нет голосов", - "one": "1 голос", - "few": "%(count)s голоса", - "many": "%(count)s голосов", - "other": "%(count)s голосов" + "g": { + "age": "возраст", + "amount": "Количество", + "and": "и", + "are_you_sure": "Вы уверены?", + "ask": "Продажа", + "balance": "Баланс", + "balances": "Балансы", + "bid": "Покупка", + "blog": "Блог", + "browse": "Посмотреть", + "buy": "Купить", + "buy_or_sell": "Купить или Продать", + "by": "от", + "cancel": "Отмена", + "change_password": "Сменить пароль", + "choose_language": "Выберите язык", + "clear": "Очистить", + "close": "Закрыть", + "collapse_or_expand": "Свернуть/Развернуть", + "comments": "Комментарии", + "confirm": "Подтвердить", + "convert": "Конвертировать", + "date": "Дата", + "delete": "Удалить", + "dismiss": "Скрыть", + "edit": "Редактировать", + "email": "Электронная почта", + "feed": "Лента", + "follow": "Подписаться", + "for": " для ", + "from": " от ", + "go_back": "Назад", + "hide": "Скрыть", + "in": "в", + "in_reply_to": "в ответ на", + "insufficient_balance": "Недостаточный баланс", + "invalid_amount": "Недостаточно средств", + "joined": "Присоединился", + "loading": "Загрузка", + "login": "Войти", + "logout": "Выйти", + "memo": "Примечание", + "mute": "Заблокировать", + "new": "новое", + "newer": "Новее", + "next": "Следующий", + "no": "Нет", + "ok": "ОК", + "older": "Старее", + "or": "или", + "order_placed": "Заказ размещен", + "password": "Пароль", + "payouts": "Выплаты", + "permissions": "Разрешения", + "phishy_message": + "Link expanded to plain text; beware of a potential phishing attempt", + "post": "Пост", + "post_as": "Запостить как", + "posts": "Посты", + "powered_up_100": "100%% в Силе Голоса", + "preview": "Предварительный просмотр", + "previous": "Предыдущий", + "price": "Цена", + "print": "Распечатать", + "promote": "Продвинуть", + "promoted": "продвигаемое", + "re": "RE", + "re_to": "RE: %(topic)s", + "recent_password": "Недавний пароль", + "receive": "Получено ", + "remove": "Удалить", + "remove_vote": "Убрать голос", + "replied_to": "ответил %(account)s", + "replies": "Ответы", + "reply": "Ответить", + "reply_count": { + "zero": "нет ответов", + "one": "1 ответ", + "few": "%(count)s ответа", + "many": "%(count)s ответов", + "other": "%(count)s ответов" + }, + "reputation": "Репутация", + "reveal_comment": "Показать комментарий", + "request": "запрос", + "required": "Обязательно", + "rewards": "вознаграждение", + "save": "Сохранить", + "saved": "Сохранено", + "search": "Поиск", + "sell": "Продать", + "settings": "Настройки", + "share_this_post": "Поделиться этим постом", + "show": "Показать", + "sign_in": "Войти", + "sign_up": "Регистрация", + "since": "начиная с", + "submit": "Отправить", + "power_up": "Усилить силу голоса", + "submit_a_story": "Добавить пост", + "tag": "Тег", + "to": " к ", + "all_tags": "All tags", + "transfer": "Перевод ", + "trending_topics": "Популярное", + "type": "Тип", + "unfollow": "Отписаться", + "unmute": "Разблокировать", + "unknown": "Неизвестно", + "upvote": "Голосовать за", + "upvote_post": "Проголосовать за пост", + "username": "Имя пользователя", + "version": "Версия", + "vote": "Проголосовать", + "votes": "голосов", + "wallet": "Кошелек", + "warning": "внимание", + "yes": "Да", + "posting": "Постинг", + "owner": "Владелец", + "active": "Активный", + "account_not_found": "Аккаунт не найден", + "this_is_wrong_password": "Это неправильный пароль", + "do_you_need_to": "Вам нужно", + "account_name": "Имя аккаунта", + "recover_your_account": "восстановить ваш аккаунт", + "reset_usernames_password": "Сбросить пароль пользователя %(username)s", + "this_will_update_usernames_authtype_key": + "Это обновит %(username)s %(authType)s ключ", + "passwords_do_not_match": "Пароли не совпадают", + "you_need_private_password_or_key_not_a_public_key": + "Вам нужен приватный пароль или ключ (не публичный ключ)", + "the_rules_of_APP_NAME": { + "one": "Первое правило сети %(APP_NAME)s: не теряйте свой пароль.", + "second": "Второе правило %(APP_NAME)s: Не теряйте свой пароль.", + "third": + "Третье правило %(APP_NAME)s: мы не можем восстановить ваш пароль.", + "fourth": + "Четвертое правило: если вы можете запомнить свой пароль, значит он не безопасен.", + "fifth": + "Пятое правило: используйте только сгенерированные случайным образом пароли.", + "sixth": "Шестое правило: Никому не говорите свой пароль.", + "seventh": "Седьмое правило: Всегда надежно храните свой пароль." + }, + "recover_password": "Восстановить аккаунт", + "current_password": "Текущий пароль", + "generated_password": "Сгенерированный пароль", + "backup_password_by_storing_it": + "Сделайте резервную копию в менеджере паролей или текстовом файле", + "enter_account_show_password": + "Введите действительное имя аккаунта, чтобы показать пароль", + "click_to_generate_password": "Нажмите, чтобы сгененировать пароль", + "re_enter_generate_password": "Повторно введите пароль", + "understand_that_APP_NAME_cannot_recover_password": + "Я понимаю, что %(APP_NAME)s не может восстановить потерянные пароли", + "i_saved_password": "Я надежно сохранил сгенерированный пароль", + "update_password": "Обновить пароль", + "confirm_password": "Подтвердить пароль", + "account_updated": "Аккаунт обновлен", + "password_must_be_characters_or_more": + "Пароль должен содержать %(amount)s символ(а) или больше", + "need_password_or_key": + "Вам нужен приватный пароль или ключ (не публичный ключ)", + "login_to_see_memo": "войти чтобы увидеть примечание", + "new_password": "Новый пароль", + "incorrect_password": "Неправильный пароль", + "username_does_not_exist": "Имя пользователя не найдено", + "account_name_should_start_with_a_letter": + "Имя аккаунта должно начинаться с буквы.", + "account_name_should_be_shorter": "Имя аккаунта должно быть короче.", + "account_name_should_be_longer": "Имя аккаунта должно быть длиннее.", + "account_name_should_have_only_letters_digits_or_dashes": + "Имя аккаунта должно должно состоять только из букв, цифр или дефисов.", + "cannot_increase_reward_of_post_within_the_last_minute_before_payout": + "Не удается увеличить вознаграждение за пост в последнюю минуту перед выплатой", + "vote_currently_exists_user_must_be_indicate_a_to_reject_witness": + "голос уже существует, пользователь должен обозначить желание убрать делегата", + "only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes": + "Только один Steem аккаунт разрешен с одного IP адреса каждые десять минут", + "resteem_this_post": "Поделиться этим постом", + "reblog": "Поделиться", + "write_your_story": "Написать свою историю", + "remember_voting_and_posting_key": "Запомнить голос и постинг ключ", + "auto_login_question_mark": "Заходить автоматически?", + "hide_private_key": "Скрыть приватный ключ", + "show_private_key": "Показать приватный ключ", + "login_to_show": "Войти, чтобы показать", + "not_valid_email": "Не действительный адрес", + "thank_you_for_being_an_early_visitor_to_APP_NAME": + "Благодарим вас за то что являетесь ранним посетителем %(APP_NAME)s. Мы свяжемся с Вами при первой же возможности.", + "author_rewards": "Авторские вознаграждения", + "curation_rewards": "Кураторские вознаграждения", + "sorry_your_reddit_account_doesnt_have_enough_karma": + "Извините, у вашего Reddit аккаунта недостаточно Reddit кармы чтобы иметь возможность бесплатной регистрации. Пожалуйста, добавьте вашу электронную почту чтобы записаться в лист ожидания", + "register_with_facebook": "Регистрация с Facebook", + "or_click_the_button_below_to_register_with_facebook": + "Или нажмите на кнопку ниже, чтобы зарегистрироваться в Facebook", + "server_returned_error": "ошибка сервера", + "APP_NAME_support": "%(APP_NAME)s поддержка", + "please_email_questions_to": + "Пожалуйста, шлите ваши вопросы на электронную почту", + "next_7_strings_single_block": { + "authors_get_paid_when_people_like_you_upvote_their_post": + "Авторы получают вознаграждение, когда пользователи голосуют за их посты", + "if_you_enjoyed_what_you_read_earn_amount": + "Если Вам понравилось то, что Вы здесь прочитали, создайте свой аккаунт сегодня и начните зарабатывать БЕСПЛАТНЫЕ STEEM-ы!", + "free_steem": "БЕСПЛАТНЫЕ STEEM!", + "sign_up_earn_steem": "Зарегистрируйтесь сейчас, чтобы заработать " + }, + "next_3_strings_together": { + "show_more": "Показать больше", + "show_less": "Показать меньше", + "value_posts": "меньше сообщений низкой стоимости" + }, + "read_only_mode": + "По техническим причинам вебсайт доступен только для чтения, приносим свои извинения.", + "tags_and_topics": "Теги и темы", + "show_more_topics": "Показать больше тем", + "basic": "Базовый", + "advanced": "Подробнее", + "views": { + "zero": "Нет просмотров", + "one": "%(count)s просмотр", + "few": "%(count)s просмотра", + "many": "%(count)s просмотров", + "other": "%(count)s просмотров" + }, + "responses": { + "zero": "Нет ответов", + "one": "%(count)s ответ", + "few": "%(count)s ответа", + "many": "%(count)s ответов", + "other": "%(count)s ответов" + }, + "post_key_warning": { + "confirm": + "Вы собираетесь опубликовать ваш приватный ключ или мастер-пароль, это может привести к потере вашего акканта и всех средств на нем.", + "warning": + "Если кто-то попросил вас опубликовать или показать ваш приватный ключ, это возможно злоумышленник, который пытается украсть ваши токены.", + "checkbox": "Я понял" + } + }, + "navigation": { + "about": "О проекте", + "explore": "Исследовать", + "APP_NAME_whitepaper": "Белая книга %(APP_NAME)s", + "buy_LIQUID_TOKEN": "Купить %(LIQUID_TOKEN)s", + "sell_LIQUID_TOKEN": "Продать %(LIQUID_TOKEN)s", + "currency_market": "Биржа", + "stolen_account_recovery": "Восстановление украденного аккаунта", + "change_account_password": "Изменить пароль аккаунта", + "witnesses": "Делегаты", + "vote_for_witnesses": "Проголосовать за делегатов", + "privacy_policy": "Политика Конфиденциальности", + "terms_of_service": "Условия пользования", + "sign_up": "Присоединиться", + "learn_more": "Документация", + "welcome": "Добро пожаловать", + "faq": "ЧаВО", + "shop": "Магазин Steemit", + "chat": "Чат Steemit", + "app_center": "Центр приложений Steemit", + "api_docs": "API-документация Steemit", + "bluepaper": "Синяя бумага Steem", + "smt_whitepaper": "Белая книга SMT", + "whitepaper": "Белая книга Steem", + "intro_tagline": "Здесь говорят деньги.", + "intro_paragraph": + "Ваше мнение имеет цену. Присоединяйтесь к сообществу, которое платит за контент и за работу по отбору самого лучшего контента." + }, + "main_menu": { + "hot": "актуальное", + "trending": "популярное" + }, + "reply_editor": { + "shorten_title": "Сократите заголовок", + "exceeds_maximum_length": "Превышает максимальную длину (%(maxKb)sKB)", + "including_the_category": "(включая категорию «%(rootCategory)s»)", + "use_limited_amount_of_tags": + "У вас %(tagsLength)s тегов, включая %(includingCategory)s. Пожалуйста, используйте не более 5 в посте и категории.", + "are_you_sure_you_want_to_clear_this_form": + "Вы уверены, что вы хотите очистить эту форму?", + "uploading": "Загрузка", + "draft_saved": "Черновик сохранен.", + "editor": "Редактор", + "insert_images_by_dragging_dropping": + "Вставьте изображения, перетащив их, ", + "pasting_from_the_clipboard": "или вставив из буфера обмена, ", + "selecting_them": "выбрав их", + "image_upload": "Загрузка изображения", + "power_up_100": "Сила Голоса 100%%", + "default_50_50": "По умолчанию (50%% / 50%%)", + "decline_payout": "Отказаться от выплаты", + "check_this_to_auto_upvote_your_post": + "Отметьте, чтобы проголосовать за свой пост", + "markdown_styling_guide": "Руководство стилизации в Markdown", + "or_by": "или", + "title": "Заголовок", + "update_post": "Update Post", + "markdown_not_supported": "Markdown здесь не поддерживается" + }, + "category_selector_jsx": { + "tag_your_story": + "Добавь теги (до 5 штук), первый тег станет основной категорией.", + "select_a_tag": "Выбрать тег", + "maximum_tag_length_is_24_characters": + "Максимальная длина категории 24 знака", + "use_limited_amount_of_categories": + "Пожалуйста, используйте только %(amount)s категории", + "use_only_lowercase_letters": + "Используйте только символы нижнего регистра", + "use_one_dash": "Используйте только одно тире", + "use_spaces_to_separate_tags": + "Используйте пробел чтобы разделить теги", + "use_only_allowed_characters": + "Используйте только строчные буквы, цифры и одно тире", + "must_start_with_a_letter": "Должно начинаться с буквы", + "must_end_with_a_letter_or_number": + "Должно заканчиваться с буквы или номера" + }, + "postfull_jsx": { + "this_post_is_not_available_due_to_a_copyright_claim": + "Этот пост недоступен из-за жалобы о нарушении авторских прав.", + "share_on_facebook": "Поделитесь в Facebook", + "share_on_twitter": "Поделиться в Twitter", + "share_on_linkedin": "Поделиться в Linkedin", + "recent_password": "Недавний пароль", + "in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN": + "В 3,5 дня перевести %(amount)s %(DEBT_TOKEN)s в %(LIQUID_TOKEN)s", + "view_the_full_context": "Показать полный контекст", + "view_the_direct_parent": "Просмотр прямого родителя", + "you_are_viewing_a_single_comments_thread_from": + "Вы читаете одну нить комментариев от" + }, + "market_jsx": { + "action": "Действие", + "date_created": "Дата создания", + "last_price": "Последняя цена", + "24h_volume": "Объем за 24 часа", + "spread": "Спред", + "total": "Итого", + "available": "Доступно", + "lowest_ask": "Лучшая цена продажи", + "highest_bid": "Лучшая цена покупки", + "buy_orders": "Заказы на покупку", + "sell_orders": "Заказы на продажу", + "trade_history": "История сделок", + "open_orders": "Открытые сделки", + "sell_amount_for_atleast": + "Продать %(amount_to_sell)s за %(min_to_receive)s по цене (%(effectivePrice)s)", + "buy_atleast_amount_for": + "Купить по крайней мере %(min_to_receive)s за %(amount_to_sell)s (%(effectivePrice)s)", + "price_warning_above": + "Эта цена намного выше текущей рыночной цены %(marketPrice)s, вы уверены?", + "price_warning_below": + "Эта цена намного выше текущей рыночной цены %(marketPrice)s, вы уверены?", + "order_cancel_confirm": "Отменить заказ %(order_id)s от %(user)s?", + "order_cancelled": "Заказ %(order_id)s отменен.", + "higher": "Дороже", + "lower": "Дешевле", + "total_DEBT_TOKEN_SHORT_CURRENCY_SIGN": + "Сумма %(DEBT_TOKEN_SHORT)s (%(CURRENCY_SIGN)s)" + }, + "recoveraccountstep1_jsx": { + "begin_recovery": "Начать восстановление", + "not_valid": "Недействительно", + "account_name_is_not_found": "Имя аккаунта не найдено", + "unable_to_recover_account_not_change_ownership_recently": + "Мы не можем восстановить эту учетную запись, она не изменила владельца в последнее время.", + "password_not_used_in_last_days": + "Этот пароль не использовался в этой учетной записи за последние 30 дней.", + "request_already_submitted_contact_support": + "Ваш запрос был отправлен, и мы работаем над этим. Пожалуйста, свяжитесь с %(SUPPORT_EMAIL)s для получения статуса вашего запроса.", + "recover_account_intro": + "Иногда бывает что ключ владельца может быть скомпрометирован. Восстановление украденного аккаунта дает законному владельцу 30 дней чтобы вернуть аккаунт с момента изменения владельческого ключа мошенником. Восстановление украденного аккаунта в %(APP_URL)s возможно только если владелец аккаунта ранее указал %(APP_NAME)s's в качестве доверенного лица и согласился с Условиями Использования сайта %(APP_NAME)s's.", + "login_with_facebook_or_reddit_media_to_verify_identity": + "Пожалуйста, войдите используя Facebook или Reddit для подтверждения вашей личности", + "login_with_social_media_to_verify_identity": + "Пожалуйста, войдите используя %(provider)s для подтверждения вашей личности", + "enter_email_toverify_identity": + "Нам нужно подтвердить вашу личность. Пожалуйста, укажите вашу электронную почту ниже для начала проверки.", + "continue_with_email": "Продолжить с электронной почтой", + "thanks_for_submitting_request_for_account_recovery": + "

        Благодарим Вас за отправку запроса на восстановление аккаунта используя основанную на блокчейне мультифакторную аутентификацию %(APP_NAME)s’a.

        Мы ответим Вам как можно быстрее, однако, пожалуйста, ожидайте что может быть некоторая задержка из-за большого объема писем.

        Пожалуйста, будьте готовы подтвердить свою личность.

        С уважением,

        Ned Scott

        CEO Steemit

        ", + "recovering_account": "Восстанавливаем аккаунт", + "recover_account": "Восстановить аккаунт", + "checking_account_owner": "Проверяем владельца аккаунта", + "sending_recovery_request": "Отправляем запрос восстановления", + "cant_confirm_account_ownership": + "Мы не можем подтвердить владение аккаунтом. Проверьте ваш пароль", + "account_recovery_request_not_confirmed": + "Запрос восстановления аккаунта еще не подтвержден, пожалуйста проверьте позднее. Спасибо за ваше терпение." + }, + "user_profile": { + "unknown_account": "Неизвестный аккаунт", + "user_hasnt_made_any_posts_yet": + "Похоже, что %(name)s еще не написал постов!", + "user_hasnt_started_bloggin_yet": + "Похоже. что %(name)s еще не завёл блог!", + "user_hasnt_followed_anything_yet": + "Похоже, что %(name)s еще ни на кого не подписан! Если, %(name)s недавно подписался на новых пользователей, их персонализированный канал будет заполняться сразу после появления нового контента.", + "user_hasnt_had_any_replies_yet": "%(name)s еще не получил ответов", + "looks_like_you_havent_posted_anything_yet": + "Похоже, ты еще ничего не опубликовал.", + "create_a_post": "Создать сообщение", + "explore_trending_articles": + "Посмотреть статьи, набирающие популярность", + "read_the_quick_start_guide": "Прочтите краткое руководство", + "browse_the_faq": "Просмотреть ЧаВО", + "followers": "Подписчики", + "this_is_users_reputations_score_it_is_based_on_history_of_votes": + "Это репутация %(name)s.\n\nРепутация рассчитывается на основе истории полученных голосов и используется для сокрытия низкокачественного контента.", + "follower_count": { + "zero": "Нет подписчиков", + "one": "1 подписчик", + "few": "%(count)s подписчика", + "many": "%(count)s подписчиков", + "other": "%(count)s подписчиков" + }, + "followed_count": { + "zero": "Ни на кого не подписан", + "one": "1 подписок", + "few": "%(count)s подписки", + "many": "%(count)s подписок", + "other": "%(count)s подписок" + }, + "post_count": { + "zero": "Постов нет", + "one": "1 пост", + "few": "%(count)s поста", + "many": "%(count)s постов", + "other": "%(count)s постов" + } + }, + "authorrewards_jsx": { + "estimated_author_rewards_last_week": + "Оценочные авторские вознаграждения за прошлую неделю", + "author_rewards_history": "История авторских наград" + }, + "curationrewards_jsx": { + "estimated_curation_rewards_last_week": + "Оценочные кураторские вознаграждения за последнюю неделю", + "curation_rewards_history": "История кураторских наград" + }, + "post_jsx": { + "now_showing_comments_with_low_ratings": + "Отображаем комментарии с низким рейтингом", + "sort_order": "Порядок сортировки", + "comments_were_hidden_due_to_low_ratings": + "Комментарии были скрыты из-за низкого рейтинга" + }, + "voting_jsx": { + "flagging_post_can_remove_rewards_the_flag_should_be_used_for_the_following": + "Голосование против уменьшает вознаграждение и снижает позицию в рейтинге.", + "disagreement_on_rewards": "Несогласие с вознаграждением", + "fraud_or_plagiarism": "Мошенничество или плагиат", + "hate_speech_or_internet_trolling": + "Разжигание ненависти или Интернет троллинг", + "intentional_miss_categorized_content_or_spam": + "Преднамеренная неправильная категоризация контента или спам", + "pending_payout": "Ожидаемая выплата", + "payout_declined": "Автор отказался от вознаграждения", + "max_accepted_payout": + "Максимально допустимое вознаграждение: $%(value)s", + "promotion_cost": "Цена продвижения: $%(value)s", + "past_payouts": "Предыдущие выплаты $%(value)s", + "past_payouts_author": " - Авторские $%(value)s", + "past_payouts_curators": " - Кураторские $%(value)s", + "we_will_reset_curation_rewards_for_this_post": + "это сбросит выплаты за курирование", + "removing_your_vote": "Удаление голоса", + "changing_to_an_upvote": "Изменить на голосование 'за'", + "changing_to_a_downvote": "Изменить на голосование 'против'", + "confirm_flag": "Подтвердить голос 'против'", + "and_more": "и %(count)s больше", + "votes_plural": { + "one": "%(count)s голоса", + "few": "%(count)s голоса", + "many": "%(count)s голосов", + "other": "%(count)s голосов" + } + }, + "witnesses_jsx": { + "witness_thread": "пост делегата", + "top_witnesses": "Топ делегатов", + "you_have_votes_remaining": { + "zero": "У Вас не осталось голосов", + "one": "У Вас остался 1 голос", + "few": "У Вас осталось %(count)s голоса", + "many": "У Вас осталось %(count)s голосов", + "other": "У Вас осталось %(count)s голосов" + }, + "you_can_vote_for_maximum_of_witnesses": + "Вы можете голосовать максимум за 30 делегатов", + "witness": "Делегаты", + "information": "Информация", + "if_you_want_to_vote_outside_of_top_enter_account_name": + "Если вы хотите проголосовать за делегата вне top 50, введите имя аккаунта ниже", + "set_witness_proxy": + "Вы также можете выбрать прокси, который будет вместо вас голосовать за делегатов. Это сбросит ваш текущий выбор делегата.", + "witness_set": + "Вы установили прокси для голосования. Если вы хотите голосовать вручную, пожалуйста, очистите ваш прокси.", + "witness_proxy_current": "Ваш текущий прокси", + "witness_proxy_set": "Установить прокси", + "witness_proxy_clear": "Очистить прокси", + "proxy_update_error": "Ваш прокси не был обновлен" + }, + "votesandcomments_jsx": { + "no_responses_yet_click_to_respond": + "Ответов пока нет. Нажмите чтобы ответить.", + "response_count_tooltip": { + "zero": "ответов пока нет. Нажмите чтобы ответить.", + "one": "1 ответ. Нажмите чтобы ответить.", + "few": "%(count)s ответа. Нажмите чтобы ответить.", + "many": "%(count)s ответов. Нажмите чтобы ответить.", + "other": "%(count)s ответов. Нажмите чтобы ответить." + }, + "vote_count": { + "zero": "нет голосов", + "one": "1 голос", + "few": "%(count)s голоса", + "many": "%(count)s голосов", + "other": "%(count)s голосов" + } + }, + "userkeys_jsx": { + "public": "Публичное", + "private": "Приватное", + "public_something_key": "Публичный %(key)s ключ", + "private_something_key": "Приватный %(key)s ключ", + "posting_key_is_required_it_should_be_different": + "Постинг ключ используется для постинга и голосования. Он должен отличаться от активного ключа и ключа владельца.", + "the_active_key_is_used_to_make_transfers_and_place_orders": + "Активный ключ используется для переводов и размещения заказов на внутреннем рынке.", + "the_owner_key_is_required_to_change_other_keys": + "Ключ владельца это главный ключ ко всему аккаунта, он необходим для изменения других ключей.", + "the_private_key_or_password_should_be_kept_offline": + "Приватный ключ или пароль должен храниться в оффлайне так часто насколько возможно.", + "the_memo_key_is_used_to_create_and_read_memos": + "Ключ примечаний используется для создания и чтения примечаний." + }, + "suggestpassword_jsx": { + "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": + "%(APP_NAME)s не может восстановить пароли. Сохраните эту страницу в безопасном месте, например, в огнестойком сейфе или в депозитарной ячейке.", + "APP_NAME_password_backup": "%(APP_NAME)s резервное копирование пароля", + "APP_NAME_password_backup_required": + "%(APP_NAME)s резервное копирование пароля (обязательно!)", + "after_printing_write_down_your_user_name": + "После печати запишите ваше имя пользователя" + }, + "converttosteem_jsx": { + "your_existing_DEBT_TOKEN_are_liquid_and_transferable": + "Ваши %(DEBT_TOKEN)s токены ликвидны. Вы можете конвертировать %(DEBT_TOKEN)s на этом сайте (%(link)s) или вывести на биржу/обменник.", + "this_is_a_price_feed_conversion": + "Конвертация на сайте выполняется три с половиной дня - это требуется, чтобы исключить манипуляции с ценами.", + "convert_to_LIQUID_TOKEN": "Ковертировать в %(LIQUID_TOKEN)s", + "DEBT_TOKEN_will_be_unavailable": + "Эта операция будет проходить 3-5 дней от настоящего момента и ее нельзя отменить. Эти %(DEBT_TOKEN)s мгновенно станут недоступны" + }, + "tips_js": { + "liquid_token": + "Ликвидные цифровые токены, которые могут переданы куда угодно в любой момент.
        %(LIQUID_TOKEN)s может быть конвертирован в %(VESTING_TOKEN)s, этот процесс называется \"увеличение Силы Голоса\".", + "influence_token": + "Токены дают вам возможность влиять на вознаграждения за контент, а также возможность зарабатывать на курации контента.", + "estimated_value": + "Сметная стоимость основана на среднем значении %(LIQUID_TOKEN)s в долларах США.", + "non_transferable": + "%(VESTING_TOKEN)s - не ликвидные токены, требуется три месяца (13 еженедальных выплат) чтобы сконвертировать их в ликвидные токены %(LIQUID_TOKEN)s.", + "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": + "Конвертированные %(VESTING_TOKEN)s токены могут быть отправлены себе или кому-либо еще, но не могут быть переданы вновь без конвертации в %(LIQUID_TOKEN)s.", + "part_of_your_steem_power_is_currently_delegated": + "Часть STEEM POWER вам делегирована, это увеличивает ваше влияние на платформе. Количество делегированных токенов со временем может изменяться." + }, + "promote_post_jsx": { + "promote_post": "Продвинуть пост", + "spend_your_DEBT_TOKEN_to_advertise_this_post": + "Используйте ваши %(DEBT_TOKEN)s чтобы прорекламировать этот пост в секции продвигаемого контента", + "you_successfully_promoted_this_post": + "Операция продвижения успешно завершена", + "this_post_was_hidden_due_to_low_ratings": + "Этот пост был скрыт из-за низкого рейтинга" + }, + "about_jsx": { + "about_app": "О %(APP_NAME)s", + "about_app_details": + "%(APP_NAME)s - это социальная медиа платформа в которой каждый зарабатывает за создание и курирование контента. Он использует надежную систему цифровых очков под названием Голос, который поддерживает реальную ценность для цифровых наград через выявление рыночной цены и ликвидности.", + "learn_more_at_app_url": "Узнать больше в %(APP_URL)s", + "resources": "Ресурсы" + }, + "markdownviewer_jsx": { + "images_were_hidden_due_to_low_ratings": + "Изображения были скрыты из-за низкого рейтинга." + }, + "postsummary_jsx": { + "resteemed": "Поделиться", + "resteemed_by": "Репостнуто", + "reveal_it": "посмотреть пост", + "adjust_your": "откорректируйте свои", + "display_preferences": "настройки отображения", + "create_an_account": "зарегистрироваться", + "to_save_your_preferences": "сохранить ваши настройки" + }, + "posts_index": { + "empty_feed_1": "Похоже, что Вы еще ни на кого не подписаны", + "empty_feed_2": + "Если Вы недавно подписались на новых пользователей, Ваша персональная лента будет заполняться, когда будет доступен новый контент", + "empty_feed_3": "Посмотреть статьи, набирающие популярность", + "empty_feed_4": "Прочтите краткое руководство", + "empty_feed_5": "Просмотреть ЧаВО" + }, + "transferhistoryrow_jsx": { + "to_savings": "в сейф", + "from_savings": "из сейфа", + "cancel_transfer_from_savings": "Отменить перевод из сейфа", + "stop_power_down": "Ослабление Силы Голоса остановлено", + "start_power_down_of": "Ослабление Силы Голоса начато с", + "receive_interest_of": "Получать проценты по" + }, + "savingswithdrawhistory_jsx": { + "cancel_this_withdraw_request": "Отменить запрос вывода средств?", + "pending_savings_withdrawals": "Выплаты из сейфа", + "withdraw": "Снять %(amount)s", + "to": "для %(to)s", + "from_to": "от %(from)s к %(to)s" + }, + "explorepost_jsx": { + "copied": "Скопировано!", + "copy": "Копировать", + "alternative_sources": "Альтернативные источники" + }, + "header_jsx": { + "home": "лента", + "create_a_post": "Создать пост", + "change_account_password": "Изменить пароль аккаунта", + "create_account": "Создать Аккаунт", + "stolen_account_recovery": "Возврат украденного аккаунта", + "people_following": "Подписчики", + "people_followed_by": "Подписчики", + "curation_rewards_by": "Награда за курирование", + "author_rewards_by": "Автор награжден", + "replies_to": "Ответы на", + "comments_by": "Комментарии" + }, + "loginform_jsx": { + "you_need_a_private_password_or_key": + "Вам нужен приватный пароль или ключ (не публичный ключ)", + "cryptography_test_failed": "Криптографический тест не пройден", + "unable_to_log_you_in": + "У нас не получится залогинить вас в этом браузере.", + "the_latest_versions_of": "Последние версии ", + "are_well_tested_and_known_to_work_with": + "хорошо протестированы и работают с %(APP_URL)s.", + "due_to_server_maintenance": + "Из-за технического обслуживания сервера мы работаем в режиме чтения. Извините за неудобства.", + "login_to_vote": "Войти, чтобы проголосовать", + "login_to_post": "Войти, чтобы написать пост", + "login_to_comment": "Войти, чтобы оставить комментарий", + "posting": "Постинг", + "active_or_owner": "Активный или Владельца", + "this_password_is_bound_to_your_account_owner_key": + "Этот пароль привязан к главному ключу аккаунта и не может быть использован для входа на этот сайт.", + "however_you_can_use_it_to": + "Тем не менее его можно использовать чтобы ", + "update_your_password": "обновить Ваш пароль", + "to_obtain_a_more_secure_set_of_keys": + "для получения более безопасного набора ключей.", + "this_password_is_bound_to_your_account_active_key": + "Этот пароль привязан к главному ключу аккаунта и не может быть использован для входа на этот сайт.", + "you_may_use_this_active_key_on_other_more": + "Вы можете использовать этот активный ключ на других более безопасных страниц, таких как страницы: Кошелек или Биржа.", + "you_account_has_been_successfully_created": + "Ваш аккаунт был успешно создан!", + "you_account_has_been_successfully_recovered": + "Ваш аккаунт был успешно восстановлен!", + "password_update_succes": + "Пароль для %(accountName)s был успешно обновлен", + "password_info": + "Этот пароль или закрытый ключ был введен неправильно. Вероятно, есть ошибка почерка или ввода данных. Подсказка: пароль или закрытый ключ, сгенерированный Steemit, никогда не будет содержать символы 0 (ноль), O (прописная o), I (прописная i) и l (нижний регистр L).", + "enter_your_username": "Введи свое имя пользователя", + "password_or_wif": "Пароль или WIF", + "this_operation_requires_your_key_or_master_password": + "Для осуществления данной операции нужен ваш %(authType)s ключ или Основной пароль.", + "keep_me_logged_in": "Оставить меня залогиненным", + "amazing_community": "удивительное сообщество", + "to_comment_and_reward_others": + " комментировать и вознаграждать других.", + "sign_up_get_steem": "Sign up. Get STEEM", + "signup_button": "Зарегистрируйтесь", + "signup_button_emphasis": " сейчас!", + "returning_users": "Вернувшиеся пользователи: ", + "join_our": "Присоединяйтесь к нашему" + }, + "chainvalidation_js": { + "account_name_should": "Имя аккаунта должно быть ", + "not_be_empty": "не может быть пустым.", + "be_longer": "длиннее.", + "be_shorter": "короче.", + "each_account_segment_should": "Имя аккаунта должно начинаться с ", + "start_with_a_letter": "должно начинаться с буквы.", + "have_only_letters_digits_or_dashes": + "должно должно состоять только из букв, цифр или дефисов.", + "have_only_one_dash_in_a_row": "иметь только одно тире в строке.", + "end_with_a_letter_or_digit": "заканчиваться буквой или цифрой.", + "verified_exchange_no_memo": + "Для перевода на биржу Вы должны указать примечание." + }, + "settings_jsx": { + "invalid_url": "Недопустимый URL-адрес", + "name_is_too_long": "Имя слишком длинное", + "name_must_not_begin_with": "Имя не должно начинаться с @", + "about_is_too_long": "Информация о Вашем блоге слишком длинная", + "location_is_too_long": "Местоположение слишком длинное", + "website_url_is_too_long": "URL-адрес веб-сайта слишком длинный", + "public_profile_settings": "Настройки Публичного Профиля", + "private_post_display_settings": + "Настройки отображения приватных постов", + "not_safe_for_work_nsfw_content": + "Небезопасный для работы (NSFW) контент", + "always_hide": "Всегда скрывать", + "always_warn": "Всегда предупреждать", + "always_show": "Отображать всегда", + "muted_users": "Заблокированные пользователи", + "update": "Обновить", + "profile_image_url": "Добавьте url вашего изображения", + "cover_image_url": "URL-адрес изображения обложки", + "profile_name": "Отображаемое имя", + "profile_about": "О себе", + "profile_location": "Местоположение", + "profile_website": "Веб-сайт" + }, + "transfer_jsx": { + "amount_is_in_form": "Сумма должна быть в формате 99999.999", + "insufficient_funds": "Недостаточно средств", + "use_only_3_digits_of_precison": "Используйте только 3 цифры точности", + "send_to_account": "Отправить аккаунту", + "asset": "Актив", + "this_memo_is_private": "Это примечание является приватным", + "this_memo_is_public": "Это примечание является публичным", + "convert_to_VESTING_TOKEN": "Перевести в %(VESTING_TOKEN)s", + "balance_subject_to_3_day_withdraw_waiting_period": + "Вывод баланса из сейфа на обычный счет, занимает 3 дня,", + "move_funds_to_another_account": + "Отправить средства на другой %(APP_NAME)s аккаунт.", + "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": + "Защитите средства от вывода 3-х дневным периодом ожидания.", + "withdraw_funds_after_the_required_3_day_waiting_period": + "Снять средства после необходимого 3 дневного периода ожидания.", + "from": "От", + "to": "Кому", + "asset_currently_collecting": + "начисляемые годовые на %(asset)s: %(interest)s%%.", + "beware_of_spam_and_phishing_links": + "Остерегайтесь спама и фишинговых ссылок в передачах. Не открывайте ссылки от пользователей, которым вы не доверяете. Не предоставляйте свои личные ключи третьим сторонам." + }, + "userwallet_jsx": { + "conversion_complete_tip": "Завершается", + "in_conversion": "%(amount)s на конвертации", + "transfer_to_savings": "Отправить в сейф", + "power_up": "Усилить силу голоса", + "power_down": "Уменьшить силу голоса", + "market": "Биржа", + "convert_to_LIQUID_TOKEN": "Перевести в %(LIQUID_TOKEN)s", + "withdraw_LIQUID_TOKEN": "Снять %(LIQUID_TOKEN)s", + "withdraw_DEBT_TOKENS": "Снять %(DEBT_TOKENS)s", + "tokens_worth_about_1_of_LIQUID_TICKER": + "Стоимость токенов $1.00 в %(LIQUID_TICKER)s, начисляемые годовые %(sbdInterest)s%%.", + "savings": "Сберегательный счет", + "estimated_account_value": "Приблизительная стоимость аккаунта", + "next_power_down_is_scheduled_to_happen": + "Следующее понижение силы голоса будет", + "transfers_are_temporary_disabled": "Переводы временно преостановлены.", + "history": "ИСТОРИЯ", + "redeem_rewards": "Получить вознаграждение", + "buy_steem_or_steem_power": "Купить STEEM или STEEM POWER" + }, + "powerdown_jsx": { + "power_down": "Уменьшить силу голоса (power down)", + "amount": "Количество", + "already_power_down": + "Вы уже запустили понижение силы голоса на %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s уже выплачено). Если вы измените количество, отсчет времени сбросится и составит 13 недель от сегодняшего дня.", + "delegating": + "Вы делегируете %(AMOUNT)s %(LIQUID_TICKER)s. Это количество заблокировано и не может быть выведено пока делегирование не будет отменено и не пройдет один полный цикл выплат вознаграждений.", + "per_week": "Это ~%(AMOUNT)s %(LIQUID_TICKER)s в неделю.", + "warning": + "Оставлять меньше чем %(AMOUNT)s %(LIQUID_TICKER)s не рекомендуется, т.к. это может остановить все транзакции с использованием это аккаунта.", + "error": "Неполучается уменьшить силу голоса (ERROR: %(MESSAGE)s)" + }, + "checkloginowner_jsx": { + "your_password_permissions_were_reduced": + "Your password permissions were reduced", + "if_you_did_not_make_this_change": + "If you did not make this change please", + "ownership_changed_on": "Владелец аккаунта или пароль были изменены ", + "deadline_for_recovery_is": "Вы можете восстановить доступ до ", + "i_understand_dont_show_again": "Понятно, больше не показывать" } - }, - "userkeys_jsx": { - "public": "Публичное", - "private": "Приватное", - "public_something_key": "Публичный %(key)s ключ", - "private_something_key": "Приватный %(key)s ключ", - "posting_key_is_required_it_should_be_different": "Постинг ключ используется для постинга и голосования. Он должен отличаться от активного ключа и ключа владельца.", - "the_active_key_is_used_to_make_transfers_and_place_orders": "Активный ключ используется для переводов и размещения заказов на внутреннем рынке.", - "the_owner_key_is_required_to_change_other_keys": "Ключ владельца это главный ключ ко всему аккаунта, он необходим для изменения других ключей.", - "the_private_key_or_password_should_be_kept_offline": "Приватный ключ или пароль должен храниться в оффлайне так часто насколько возможно.", - "the_memo_key_is_used_to_create_and_read_memos": "Ключ примечаний используется для создания и чтения примечаний." - }, - "suggestpassword_jsx": { - "APP_NAME_cannot_recover_passwords_keep_this_page_in_a_secure_location": "%(APP_NAME)s не может восстановить пароли. Сохраните эту страницу в безопасном месте, например, в огнестойком сейфе или в депозитарной ячейке.", - "APP_NAME_password_backup": "%(APP_NAME)s резервное копирование пароля", - "APP_NAME_password_backup_required": "%(APP_NAME)s резервное копирование пароля (обязательно!)", - "after_printing_write_down_your_user_name": "После печати запишите ваше имя пользователя" - }, - "converttosteem_jsx": { - "your_existing_DEBT_TOKEN_are_liquid_and_transferable": "Ваши %(DEBT_TOKEN)s токены ликвидны. Вы можете конвертировать %(DEBT_TOKEN)s на этом сайте (%(link)s) или вывести на биржу/обменник.", - "this_is_a_price_feed_conversion": "Конвертация на сайте выполняется три с половиной дня - это требуется, чтобы исключить манипуляции с ценами.", - "convert_to_LIQUID_TOKEN": "Ковертировать в %(LIQUID_TOKEN)s", - "DEBT_TOKEN_will_be_unavailable": "Эта операция будет проходить 3-5 дней от настоящего момента и ее нельзя отменить. Эти %(DEBT_TOKEN)s мгновенно станут недоступны" - }, - "tips_js": { - "liquid_token": "Ликвидные цифровые токены, которые могут переданы куда угодно в любой момент.
        %(LIQUID_TOKEN)s может быть конвертирован в %(VESTING_TOKEN)s, этот процесс называется \"увеличение Силы Голоса\".", - "influence_token": "Токены дают вам возможность влиять на вознаграждения за контент, а также возможность зарабатывать на курации контента.", - "estimated_value": "Сметная стоимость основана на среднем значении %(LIQUID_TOKEN)s в долларах США.", - "non_transferable": "%(VESTING_TOKEN)s - не ликвидные токены, требуется три месяца (13 еженедальных выплат) чтобы сконвертировать их в ликвидные токены %(LIQUID_TOKEN)s.", - "converted_VESTING_TOKEN_can_be_sent_to_yourself_but_can_not_transfer_again": "Конвертированные %(VESTING_TOKEN)s токены могут быть отправлены себе или кому-либо еще, но не могут быть переданы вновь без конвертации в %(LIQUID_TOKEN)s.", - "part_of_your_steem_power_is_currently_delegated": "Часть STEEM POWER вам делегирована, это увеличивает ваше влияние на платформе. Количество делегированных токенов со временем может изменяться." - }, - "promote_post_jsx": { - "promote_post": "Продвинуть пост", - "spend_your_DEBT_TOKEN_to_advertise_this_post": "Используйте ваши %(DEBT_TOKEN)s чтобы прорекламировать этот пост в секции продвигаемого контента", - "you_successfully_promoted_this_post": "Операция продвижения успешно завершена", - "this_post_was_hidden_due_to_low_ratings": "Этот пост был скрыт из-за низкого рейтинга" - }, - "about_jsx": { - "about_app": "О %(APP_NAME)s", - "about_app_details": "%(APP_NAME)s - это социальная медиа платформа в которой каждый зарабатывает за создание и курирование контента. Он использует надежную систему цифровых очков под названием Голос, который поддерживает реальную ценность для цифровых наград через выявление рыночной цены и ликвидности.", - "learn_more_at_app_url": "Узнать больше в %(APP_URL)s", - "resources": "Ресурсы" - }, - "markdownviewer_jsx": { - "images_were_hidden_due_to_low_ratings": "Изображения были скрыты из-за низкого рейтинга." - }, - "postsummary_jsx": { - "resteemed": "Поделиться", - "resteemed_by": "Репостнуто", - "reveal_it": "посмотреть пост", - "adjust_your": "откорректируйте свои", - "display_preferences": "настройки отображения", - "create_an_account": "зарегистрироваться", - "to_save_your_preferences": "сохранить ваши настройки" - }, - "posts_index": { - "empty_feed_1": "Похоже, что Вы еще ни на кого не подписаны", - "empty_feed_2": "Если Вы недавно подписались на новых пользователей, Ваша персональная лента будет заполняться, когда будет доступен новый контент", - "empty_feed_3": "Посмотреть статьи, набирающие популярность", - "empty_feed_4": "Прочтите краткое руководство", - "empty_feed_5": "Просмотреть ЧаВО" - }, - "transferhistoryrow_jsx": { - "to_savings": "в сейф", - "from_savings": "из сейфа", - "cancel_transfer_from_savings": "Отменить перевод из сейфа", - "stop_power_down": "Ослабление Силы Голоса остановлено", - "start_power_down_of": "Ослабление Силы Голоса начато с", - "receive_interest_of": "Получать проценты по" - }, - "savingswithdrawhistory_jsx": { - "cancel_this_withdraw_request": "Отменить запрос вывода средств?", - "pending_savings_withdrawals": "Выплаты из сейфа", - "withdraw": "Снять %(amount)s", - "to": "для %(to)s", - "from_to": "от %(from)s к %(to)s" - }, - "explorepost_jsx": { - "copied": "Скопировано!", - "copy": "Копировать", - "alternative_sources": "Альтернативные источники" - }, - "header_jsx": { - "home": "лента", - "create_a_post": "Создать пост", - "change_account_password": "Изменить пароль аккаунта", - "create_account": "Создать Аккаунт", - "stolen_account_recovery": "Возврат украденного аккаунта", - "people_following": "Подписчики", - "people_followed_by": "Подписчики", - "curation_rewards_by": "Награда за курирование", - "author_rewards_by": "Автор награжден", - "replies_to": "Ответы на", - "comments_by": "Комментарии" - }, - "loginform_jsx": { - "you_need_a_private_password_or_key": "Вам нужен приватный пароль или ключ (не публичный ключ)", - "cryptography_test_failed": "Криптографический тест не пройден", - "unable_to_log_you_in": "У нас не получится залогинить вас в этом браузере.", - "the_latest_versions_of": "Последние версии ", - "are_well_tested_and_known_to_work_with": "хорошо протестированы и работают с %(APP_URL)s.", - "due_to_server_maintenance": "Из-за технического обслуживания сервера мы работаем в режиме чтения. Извините за неудобства.", - "login_to_vote": "Войти, чтобы проголосовать", - "login_to_post": "Войти, чтобы написать пост", - "login_to_comment": "Войти, чтобы оставить комментарий", - "posting": "Постинг", - "active_or_owner": "Активный или Владельца", - "this_password_is_bound_to_your_account_owner_key": "Этот пароль привязан к главному ключу аккаунта и не может быть использован для входа на этот сайт.", - "however_you_can_use_it_to": "Тем не менее его можно использовать чтобы ", - "update_your_password": "обновить Ваш пароль", - "to_obtain_a_more_secure_set_of_keys": "для получения более безопасного набора ключей.", - "this_password_is_bound_to_your_account_active_key": "Этот пароль привязан к главному ключу аккаунта и не может быть использован для входа на этот сайт.", - "you_may_use_this_active_key_on_other_more": "Вы можете использовать этот активный ключ на других более безопасных страниц, таких как страницы: Кошелек или Биржа.", - "you_account_has_been_successfully_created": "Ваш аккаунт был успешно создан!", - "you_account_has_been_successfully_recovered": "Ваш аккаунт был успешно восстановлен!", - "password_update_succes": "Пароль для %(accountName)s был успешно обновлен", - "password_info": "Этот пароль или закрытый ключ был введен неправильно. Вероятно, есть ошибка почерка или ввода данных. Подсказка: пароль или закрытый ключ, сгенерированный Steemit, никогда не будет содержать символы 0 (ноль), O (прописная o), I (прописная i) и l (нижний регистр L).", - "enter_your_username": "Введи свое имя пользователя", - "password_or_wif": "Пароль или WIF", - "this_operation_requires_your_key_or_master_password": "Для осуществления данной операции нужен ваш %(authType)s ключ или Основной пароль.", - "keep_me_logged_in": "Оставить меня залогиненным", - "amazing_community": "удивительное сообщество", - "to_comment_and_reward_others": " комментировать и вознаграждать других.", - "sign_up_get_steem": "Sign up. Get STEEM", - "signup_button": "Зарегистрируйтесь", - "signup_button_emphasis": " сейчас!", - "returning_users": "Вернувшиеся пользователи: ", - "join_our": "Присоединяйтесь к нашему" - }, - "chainvalidation_js": { - "account_name_should": "Имя аккаунта должно быть ", - "not_be_empty": "не может быть пустым.", - "be_longer": "длиннее.", - "be_shorter": "короче.", - "each_account_segment_should": "Имя аккаунта должно начинаться с ", - "start_with_a_letter": "должно начинаться с буквы.", - "have_only_letters_digits_or_dashes": "должно должно состоять только из букв, цифр или дефисов.", - "have_only_one_dash_in_a_row": "иметь только одно тире в строке.", - "end_with_a_letter_or_digit": "заканчиваться буквой или цифрой.", - "verified_exchange_no_memo": "Для перевода на биржу Вы должны указать примечание." - }, - "settings_jsx": { - "invalid_url": "Недопустимый URL-адрес", - "name_is_too_long": "Имя слишком длинное", - "name_must_not_begin_with": "Имя не должно начинаться с @", - "about_is_too_long": "Информация о Вашем блоге слишком длинная", - "location_is_too_long": "Местоположение слишком длинное", - "website_url_is_too_long": "URL-адрес веб-сайта слишком длинный", - "public_profile_settings": "Настройки Публичного Профиля", - "private_post_display_settings": "Настройки отображения приватных постов", - "not_safe_for_work_nsfw_content": "Небезопасный для работы (NSFW) контент", - "always_hide": "Всегда скрывать", - "always_warn": "Всегда предупреждать", - "always_show": "Отображать всегда", - "muted_users": "Заблокированные пользователи", - "update": "Обновить", - "profile_image_url": "Добавьте url вашего изображения", - "cover_image_url": "URL-адрес изображения обложки", - "profile_name": "Отображаемое имя", - "profile_about": "О себе", - "profile_location": "Местоположение", - "profile_website": "Веб-сайт" - }, - "transfer_jsx": { - "amount_is_in_form": "Сумма должна быть в формате 99999.999", - "insufficient_funds": "Недостаточно средств", - "use_only_3_digits_of_precison": "Используйте только 3 цифры точности", - "send_to_account": "Отправить аккаунту", - "asset": "Актив", - "this_memo_is_private": "Это примечание является приватным", - "this_memo_is_public": "Это примечание является публичным", - "convert_to_VESTING_TOKEN": "Перевести в %(VESTING_TOKEN)s", - "balance_subject_to_3_day_withdraw_waiting_period": "Вывод баланса из сейфа на обычный счет, занимает 3 дня,", - "move_funds_to_another_account": "Отправить средства на другой %(APP_NAME)s аккаунт.", - "protect_funds_by_requiring_a_3_day_withdraw_waiting_period": "Защитите средства от вывода 3-х дневным периодом ожидания.", - "withdraw_funds_after_the_required_3_day_waiting_period": "Снять средства после необходимого 3 дневного периода ожидания.", - "from": "От", - "to": "Кому", - "asset_currently_collecting": "начисляемые годовые на %(asset)s: %(interest)s%%.", - "beware_of_spam_and_phishing_links": "Остерегайтесь спама и фишинговых ссылок в передачах. Не открывайте ссылки от пользователей, которым вы не доверяете. Не предоставляйте свои личные ключи третьим сторонам." - }, - "userwallet_jsx": { - "conversion_complete_tip": "Завершается", - "in_conversion": "%(amount)s на конвертации", - "transfer_to_savings": "Отправить в сейф", - "power_up": "Усилить силу голоса", - "power_down": "Уменьшить силу голоса", - "market": "Биржа", - "convert_to_LIQUID_TOKEN": "Перевести в %(LIQUID_TOKEN)s", - "withdraw_LIQUID_TOKEN": "Снять %(LIQUID_TOKEN)s", - "withdraw_DEBT_TOKENS": "Снять %(DEBT_TOKENS)s", - "tokens_worth_about_1_of_LIQUID_TICKER": "Стоимость токенов $1.00 в %(LIQUID_TICKER)s, начисляемые годовые %(sbdInterest)s%%.", - "savings": "Сберегательный счет", - "estimated_account_value": "Приблизительная стоимость аккаунта", - "next_power_down_is_scheduled_to_happen": "Следующее понижение силы голоса будет", - "transfers_are_temporary_disabled": "Переводы временно преостановлены.", - "history": "ИСТОРИЯ", - "redeem_rewards": "Получить вознаграждение", - "buy_steem_or_steem_power": "Купить STEEM или STEEM POWER" - }, - "powerdown_jsx": { - "power_down": "Уменьшить силу голоса (power down)", - "amount": "Количество", - "already_power_down": "Вы уже запустили понижение силы голоса на %(AMOUNT)s %(LIQUID_TICKER)s (%(WITHDRAWN)s %(LIQUID_TICKER)s уже выплачено). Если вы измените количество, отсчет времени сбросится и составит 13 недель от сегодняшего дня.", - "delegating": "Вы делегируете %(AMOUNT)s %(LIQUID_TICKER)s. Это количество заблокировано и не может быть выведено пока делегирование не будет отменено и не пройдет один полный цикл выплат вознаграждений.", - "per_week": "Это ~%(AMOUNT)s %(LIQUID_TICKER)s в неделю.", - "warning": "Оставлять меньше чем %(AMOUNT)s %(LIQUID_TICKER)s не рекомендуется, т.к. это может остановить все транзакции с использованием это аккаунта.", - "error": "Неполучается уменьшить силу голоса (ERROR: %(MESSAGE)s)" - }, - "checkloginowner_jsx": { - "your_password_permissions_were_reduced": "Your password permissions were reduced", - "if_you_did_not_make_this_change": "If you did not make this change please", - "ownership_changed_on": "Владелец аккаунта или пароль были изменены ", - "deadline_for_recovery_is": "Вы можете восстановить доступ до ", - "i_understand_dont_show_again": "Понятно, больше не показывать" - } } diff --git a/src/app/redux/AppReducer.js b/src/app/redux/AppReducer.js index 2d52a7ae3..de9d5adbe 100644 --- a/src/app/redux/AppReducer.js +++ b/src/app/redux/AppReducer.js @@ -1,4 +1,4 @@ -import {Map, OrderedMap} from 'immutable'; +import { Map, OrderedMap } from 'immutable'; import tt from 'counterpart'; // Action constants @@ -29,14 +29,14 @@ const defaultState = Map({ reply: 0, account_update: 0, message: 0, - receive: 0 + receive: 0, }), user_preferences: Map({ locale: null, nsfwPref: 'warn', nightmode: false, blogmode: false, - currency: 'USD' + currency: 'USD', }), }); @@ -58,14 +58,16 @@ export default function reducer(state = defaultState, action) { const n = { action: tt('g.dismiss'), dismissAfter: 10000, - ...action.payload + ...action.payload, }; - return state.update('notifications', (s) => { - return s ? s.set(n.key, n) : OrderedMap({[n.key]: n}); + return state.update('notifications', s => { + return s ? s.set(n.key, n) : OrderedMap({ [n.key]: n }); }); } case REMOVE_NOTIFICATION: - return state.update('notifications', s => s.delete(action.payload.key)); + return state.update('notifications', s => + s.delete(action.payload.key) + ); case UPDATE_NOTIFICOUNTERS: { if (action.payload) { const nc = action.payload; @@ -80,15 +82,21 @@ export default function reducer(state = defaultState, action) { case SET_USER_PREFERENCES: return state.set('user_preferences', Map(action.payload)); case TOGGLE_NIGHTMODE: - return state.setIn(['user_preferences', 'nightmode'], !state.getIn(['user_preferences', 'nightmode'])); + return state.setIn( + ['user_preferences', 'nightmode'], + !state.getIn(['user_preferences', 'nightmode']) + ); case TOGGLE_BLOGMODE: - return state.setIn(['user_preferences', 'blogmode'], !state.getIn(['user_preferences', 'blogmode'])); + return state.setIn( + ['user_preferences', 'blogmode'], + !state.getIn(['user_preferences', 'blogmode']) + ); default: return state; } } -export const steemApiError = (error) => ({ +export const steemApiError = error => ({ type: STEEM_API_ERROR, error, }); @@ -101,22 +109,22 @@ export const fetchDataEnd = () => ({ type: FETCH_DATA_END, }); -export const addNotification = (payload) => ({ +export const addNotification = payload => ({ type: ADD_NOTIFICATION, payload, }); -export const removeNotification = (payload) => ({ +export const removeNotification = payload => ({ type: REMOVE_NOTIFICATION, payload, }); -export const updateNotificounters = (payload) => ({ +export const updateNotificounters = payload => ({ type: UPDATE_NOTIFICOUNTERS, payload, }); -export const setUserPreferences = (payload) => ({ +export const setUserPreferences = payload => ({ type: SET_USER_PREFERENCES, payload, }); diff --git a/src/app/redux/AuthSaga.js b/src/app/redux/AuthSaga.js index bc56fe844..a2f6f10ce 100644 --- a/src/app/redux/AuthSaga.js +++ b/src/app/redux/AuthSaga.js @@ -1,49 +1,65 @@ -import {takeEvery} from 'redux-saga'; -import {call, put, select} from 'redux-saga/effects'; -import {Set, Map, fromJS, List} from 'immutable' -import {api} from '@steemit/steem-js'; -import {PrivateKey} from '@steemit/steem-js/lib/auth/ecc'; +import { takeEvery } from 'redux-saga'; +import { call, put, select } from 'redux-saga/effects'; +import { Set, Map, fromJS, List } from 'immutable'; +import { api } from '@steemit/steem-js'; +import { PrivateKey } from '@steemit/steem-js/lib/auth/ecc'; -import {getAccount} from 'app/redux/SagaShared'; +import { getAccount } from 'app/redux/SagaShared'; import * as userActions from 'app/redux/UserReducer'; // operations that require only posting authority -const postingOps = Set(`vote, comment, delete_comment, custom_json, claim_reward_balance`.trim().split(/,\s*/)) +const postingOps = Set( + `vote, comment, delete_comment, custom_json, claim_reward_balance` + .trim() + .split(/,\s*/) +); -export const authWatches = [ - watchForAuth -] +export const authWatches = [watchForAuth]; function* watchForAuth() { yield* takeEvery('user/ACCOUNT_AUTH_LOOKUP', accountAuthLookup); } -export function* accountAuthLookup({payload: {account, private_keys, login_owner_pubkey}}) { - account = fromJS(account) - private_keys = fromJS(private_keys) +export function* accountAuthLookup({ + payload: { account, private_keys, login_owner_pubkey }, +}) { + account = fromJS(account); + private_keys = fromJS(private_keys); // console.log('accountAuthLookup', account.name) - const stateUser = yield select(state => state.user) - let keys - if (private_keys) - keys = private_keys - else - keys = stateUser.getIn(['current', 'private_keys']) - - if (!keys || !keys.has('posting_private')) return - const toPub = k => k ? k.toPublicKey().toString() : '-' - const posting = keys.get('posting_private') - const active = keys.get('active_private') - const memo = keys.get('memo_private') + const stateUser = yield select(state => state.user); + let keys; + if (private_keys) keys = private_keys; + else keys = stateUser.getIn(['current', 'private_keys']); + + if (!keys || !keys.has('posting_private')) return; + const toPub = k => (k ? k.toPublicKey().toString() : '-'); + const posting = keys.get('posting_private'); + const active = keys.get('active_private'); + const memo = keys.get('memo_private'); const auth = { - posting: posting ? yield authorityLookup( - {pubkeys: Set([toPub(posting)]), authority: account.get('posting'), authType: 'posting'}) : 'none', - active: active ? yield authorityLookup( - {pubkeys: Set([toPub(active)]), authority: account.get('active'), authType: 'active'}) : 'none', + posting: posting + ? yield authorityLookup({ + pubkeys: Set([toPub(posting)]), + authority: account.get('posting'), + authType: 'posting', + }) + : 'none', + active: active + ? yield authorityLookup({ + pubkeys: Set([toPub(active)]), + authority: account.get('active'), + authType: 'active', + }) + : 'none', owner: 'none', - memo: account.get('memo_key') === toPub(memo) ? 'full' : 'none' - } - const accountName = account.get('name') - const pub_keys_used = {posting: toPub(posting), active: toPub(active), owner: login_owner_pubkey}; + memo: account.get('memo_key') === toPub(memo) ? 'full' : 'none', + }; + const accountName = account.get('name'); + const pub_keys_used = { + posting: toPub(posting), + active: toPub(active), + owner: login_owner_pubkey, + }; yield put(userActions.setAuthority({ accountName, auth, pub_keys_used })); } @@ -53,91 +69,102 @@ export function* accountAuthLookup({payload: {account, private_keys, login_owner @arg {object} data.pubkeys Immutable Set public key strings @return {string} full, partial, none */ -function* authorityLookup({pubkeys, authority, authType}) { - return yield call(authStr, {pubkeys, authority, authType}) +function* authorityLookup({ pubkeys, authority, authType }) { + return yield call(authStr, { pubkeys, authority, authType }); } -function* authStr({pubkeys, authority, authType, recurse = 1}) { - const t = yield call(threshold, {pubkeys, authority, authType, recurse}) - const r = authority.get('weight_threshold') - return t >= r ? 'full' : t > 0 ? 'partial' : 'none' +function* authStr({ pubkeys, authority, authType, recurse = 1 }) { + const t = yield call(threshold, { pubkeys, authority, authType, recurse }); + const r = authority.get('weight_threshold'); + return t >= r ? 'full' : t > 0 ? 'partial' : 'none'; } -export function* threshold({pubkeys, authority, authType, recurse = 1}) { - if (!pubkeys.size) return 0 - let t = pubkeyThreshold({pubkeys, authority}) - const account_auths = authority.get('account_auths') - const aaNames = account_auths.map(v => v.get(0), List()) +export function* threshold({ pubkeys, authority, authType, recurse = 1 }) { + if (!pubkeys.size) return 0; + let t = pubkeyThreshold({ pubkeys, authority }); + const account_auths = authority.get('account_auths'); + const aaNames = account_auths.map(v => v.get(0), List()); if (aaNames.size) { - const aaAccounts = yield api.getAccountsAsync(aaNames) - const aaThreshes = account_auths.map(v => v.get(1), List()) + const aaAccounts = yield api.getAccountsAsync(aaNames); + const aaThreshes = account_auths.map(v => v.get(1), List()); for (let i = 0; i < aaAccounts.size; i++) { - const aaAccount = aaAccounts.get(i) - t += pubkeyThreshold({authority: aaAccount.get(authType), pubkeys}) + const aaAccount = aaAccounts.get(i); + t += pubkeyThreshold({ + authority: aaAccount.get(authType), + pubkeys, + }); if (recurse <= 2) { - const auth = yield call(authStr, - {authority: aaAccount, pubkeys, recurse: ++recurse}) + const auth = yield call(authStr, { + authority: aaAccount, + pubkeys, + recurse: ++recurse, + }); if (auth === 'full') { - const aaThresh = aaThreshes.get(i) - t += aaThresh + const aaThresh = aaThreshes.get(i); + t += aaThresh; } } } } - return t + return t; } -function pubkeyThreshold({pubkeys, authority}) { - let available = 0 - const key_auths = authority.get('key_auths') +function pubkeyThreshold({ pubkeys, authority }) { + let available = 0; + const key_auths = authority.get('key_auths'); key_auths.forEach(k => { if (pubkeys.has(k.get(0))) { - available += k.get(1) + available += k.get(1); } - }) - return available + }); + return available; } -export function* findSigningKey({opType, username, password}) { - let authTypes - if (postingOps.has(opType)) - authTypes = 'posting, active' - else - authTypes = 'active, owner' - authTypes = authTypes.split(', ') +export function* findSigningKey({ opType, username, password }) { + let authTypes; + if (postingOps.has(opType)) authTypes = 'posting, active'; + else authTypes = 'active, owner'; + authTypes = authTypes.split(', '); - const currentUser = yield select(state => state.user.get('current')) - const currentUsername = currentUser && currentUser.get('username') + const currentUser = yield select(state => state.user.get('current')); + const currentUsername = currentUser && currentUser.get('username'); - username = username || currentUsername - if (!username) return null + username = username || currentUsername; + if (!username) return null; - const private_keys = currentUsername === username ? currentUser.get('private_keys') : Map() + const private_keys = + currentUsername === username ? currentUser.get('private_keys') : Map(); const account = yield call(getAccount, username); - if (!account) throw new Error('Account not found') + if (!account) throw new Error('Account not found'); for (const authType of authTypes) { - let private_key + let private_key; if (password) { try { - private_key = PrivateKey.fromWif(password) + private_key = PrivateKey.fromWif(password); } catch (e) { - private_key = PrivateKey.fromSeed(username + authType + password) + private_key = PrivateKey.fromSeed( + username + authType + password + ); } } else { - if(private_keys) - private_key = private_keys.get(authType + '_private') + if (private_keys) + private_key = private_keys.get(authType + '_private'); } if (private_key) { - const pubkey = private_key.toPublicKey().toString() - const pubkeys = Set([pubkey]) - const authority = account.get(authType) - const auth = yield call(authorityLookup, {pubkeys, authority, authType}) - if (auth === 'full') return private_key + const pubkey = private_key.toPublicKey().toString(); + const pubkeys = Set([pubkey]); + const authority = account.get(authType); + const auth = yield call(authorityLookup, { + pubkeys, + authority, + authType, + }); + if (auth === 'full') return private_key; } } - return null + return null; } // function isPostingOnlyKey(pubkey, account) { diff --git a/src/app/redux/DemoState.js b/src/app/redux/DemoState.js index 86aaac51c..547e19299 100644 --- a/src/app/redux/DemoState.js +++ b/src/app/redux/DemoState.js @@ -9,9 +9,9 @@ module.exports = { server_response: null, error: null, confirmed: { - blocknum: 1234 - } - } + blocknum: 1234, + }, + }, }, wallet: { locked: false, @@ -20,12 +20,12 @@ module.exports = { $pubkey: 'privkey', $pubkey1: 'privkey1', $pubkey2: 'privkey2', - $pubkey3: 'privkey3' + $pubkey3: 'privkey3', }, encrypted_keys: { - $pubkey: 'encryptedprivkey' - } - } + $pubkey: 'encryptedprivkey', + }, + }, }, users: { alice: { @@ -36,90 +36,63 @@ module.exports = { transfer: [], vote: [], post: [], - reward: [] + reward: [], }, posts: { recent: ['slug', 'slug1'], expiring: ['slug', 'slug'], - best: ['slug', 'slug'] + best: ['slug', 'slug'], }, proxy: null, - witness_votes: [] - } - } + witness_votes: [], + }, + }, }, discussions: { - update_status: { /// used to track async state of fetching + update_status: { + /// used to track async state of fetching trending: { last_update: new Date(), fetching: false, timeout: new Date(), - fetch_cursor: null /// fetching from start, else author/slug + fetch_cursor: null, /// fetching from start, else author/slug }, recent: {}, expiring: {}, best: {}, active: {}, category: { - general: { /// the category name - trending: { /// ~trending within category + general: { + /// the category name + trending: { + /// ~trending within category last_update: new Date(), fetching: false, timeout: new Date(), - fetch_cursor: null /// fetching from start, else author/slug + fetch_cursor: null, /// fetching from start, else author/slug }, recent: {}, expiring: {}, - best: {} - } - } + best: {}, + }, + }, }, - trending: [ - 'author/slug', - 'author3/slug' - ], - recent: [ - 'author3/slug', - 'author/slug' - ], - expiring: [ - 'author3/slug', - 'author/slug' - ], - best: [ - 'author2/slug', - 'author/slug' - ], - active: [ - 'author1/slug', - 'author/slug' - ], + trending: ['author/slug', 'author3/slug'], + recent: ['author3/slug', 'author/slug'], + expiring: ['author3/slug', 'author/slug'], + best: ['author2/slug', 'author/slug'], + active: ['author1/slug', 'author/slug'], category: { '~trending': ['cat1', 'cat2'], /// used to track trending categories '~best': ['bestcat1', 'bestcat2'], /// used to track all time best categories '~active': [], general: { - trending: [ - 'author/slug', - 'author3/slug' - ], - recent: [ - 'author2/slug', - 'author3/slug' - ], - expiring: [ - 'author1/slug', - 'author/slug' - ], - best: [ - 'author2/slug', - 'author3/slug' - ], - active: [ - 'author2/slug', - 'author3/slug' - ] - } + trending: ['author/slug', 'author3/slug'], + recent: ['author2/slug', 'author3/slug'], + expiring: ['author1/slug', 'author/slug'], + best: ['author2/slug', 'author3/slug'], + active: ['author2/slug', 'author3/slug'], + }, }, 'author/slug': { fetched: new Date(), /// the date at which this data was requested from the server @@ -146,49 +119,50 @@ module.exports = { total_pending_payout_value: '0.000 CLOUT', replies: [], /// there is data to be fetched if 'children' is not 0 fetched_replies: new Date(), - fetching_replies: false - } + fetching_replies: false, + }, }, market: { - current_feed: 1.00, + current_feed: 1.0, feed_history: [ /// last week of feed data with 1 hr sampling of median feed ], order_history: [ - ['time', 'buy', 1000, 1.000], /// time, type, quantity, price - ['time', 'sell', 100, 0.99] + ['time', 'buy', 1000, 1.0], /// time, type, quantity, price + ['time', 'sell', 100, 0.99], ], available_candlesticks: [5, 15, 30, 120, 240, 1440], - available_zoom: [6, 24, 48, 96, 168/* 1 week*/, 540, 1000000/*all*/], /// hours + available_zoom: [6, 24, 48, 96, 168 /* 1 week*/, 540, 1000000 /*all*/], /// hours current_candlestick: 5, /// min current_zoom: 24, /// hours price_history: [ { time: '2016-02-29T22:08:00', - open: 1.000, - close: 1.000, - high: 1.000, - low: 1.000, - volume: 10 + open: 1.0, + close: 1.0, + high: 1.0, + low: 1.0, + volume: 10, }, { time: '2016-02-29T22:09:00', - open: 1.000, - close: 1.000, - high: 1.000, - low: 1.000, - volume: 10 - } - ] + open: 1.0, + close: 1.0, + high: 1.0, + low: 1.0, + volume: 10, + }, + ], }, - bids: [ /// sorted by price from highest to lowest + bids: [ + /// sorted by price from highest to lowest { id: '...', owner: 'alice', price: 1.0, quantity: 100, cum_quantity: 100, - expiration: null + expiration: null, }, { id: '...', @@ -196,17 +170,18 @@ module.exports = { price: 0.9, quantity: 100, cum_quantity: 200, - expiration: null - } + expiration: null, + }, ], - asks: [ /// sorted by price from lowest to highest + asks: [ + /// sorted by price from lowest to highest { id: '...', owner: 'alice', price: 1.1, bid_quantity: 100, cum_quantity: 100, - expiration: null + expiration: null, }, { id: '...', @@ -214,7 +189,7 @@ module.exports = { price: 1.2, bid_quantity: 100, cum_quantity: 200, - expiration: null - } - ] + expiration: null, + }, + ], }; diff --git a/src/app/redux/EmptyState.js b/src/app/redux/EmptyState.js index ad22649eb..56f6b693a 100644 --- a/src/app/redux/EmptyState.js +++ b/src/app/redux/EmptyState.js @@ -1,7 +1,7 @@ /* Stub content (or objects) that may be inserted into the UI before being accepted by the blockchain. */ //TODO! -import { LIQUID_TICKER, DEBT_TICKER } from 'app/client_config' +import { LIQUID_TICKER, DEBT_TICKER } from 'app/client_config'; export const emptyContent = { fetched: new Date(), /// the date at which this data was requested from the server id: '2.8.0', @@ -23,9 +23,9 @@ export const emptyContent = { abs_rshares: 0, cashout_time: new Date().toISOString(), total_vote_weight: '0', - total_payout_value: ['0.000', DEBT_TICKER].join(" "), - pending_payout_value: ['0.000', LIQUID_TICKER].join(" "), - total_pending_payout_value: ['0.000', LIQUID_TICKER].join(" "), + total_payout_value: ['0.000', DEBT_TICKER].join(' '), + pending_payout_value: ['0.000', LIQUID_TICKER].join(' '), + total_pending_payout_value: ['0.000', LIQUID_TICKER].join(' '), active_votes: [], replies: [], stats: { @@ -35,4 +35,4 @@ export const emptyContent = { allowDelete: true, hide: false, }, -} +}; diff --git a/src/app/redux/FetchDataSaga.js b/src/app/redux/FetchDataSaga.js index d3076ef58..c1b184f1e 100644 --- a/src/app/redux/FetchDataSaga.js +++ b/src/app/redux/FetchDataSaga.js @@ -1,18 +1,24 @@ -import {takeLatest, takeEvery} from 'redux-saga'; -import {call, put, select, fork} from 'redux-saga/effects'; -import {loadFollows, fetchFollowCount} from 'app/redux/FollowSaga'; -import {getContent} from 'app/redux/SagaShared'; +import { takeLatest, takeEvery } from 'redux-saga'; +import { call, put, select, fork } from 'redux-saga/effects'; +import { loadFollows, fetchFollowCount } from 'app/redux/FollowSaga'; +import { getContent } from 'app/redux/SagaShared'; import * as globalActions from './GlobalReducer'; import * as appActions from './AppReducer'; import constants from './constants'; -import {fromJS, Map} from 'immutable' -import {api} from '@steemit/steem-js'; +import { fromJS, Map } from 'immutable'; +import { api } from '@steemit/steem-js'; const REQUEST_DATA = 'fetchDataSaga/REQUEST_DATA'; const GET_CONTENT = 'fetchDataSaga/GET_CONTENT'; const FETCH_STATE = 'fetchDataSaga/FETCH_STATE'; -export const fetchDataWatches = [watchLocationChange, watchDataRequests, watchFetchJsonRequests, watchFetchState, watchGetContent]; +export const fetchDataWatches = [ + watchLocationChange, + watchDataRequests, + watchFetchJsonRequests, + watchFetchState, + watchGetContent, +]; export function* watchDataRequests() { yield* takeLatest(REQUEST_DATA, fetchData); @@ -28,32 +34,36 @@ export function* getContentCaller(action) { let is_initial_state = true; export function* fetchState(location_change_action) { - const {pathname} = location_change_action.payload; - const m = pathname.match(/^\/@([a-z0-9\.-]+)/) - if(m && m.length === 2) { - const username = m[1] - yield fork(fetchFollowCount, username) - yield fork(loadFollows, "getFollowersAsync", username, 'blog') - yield fork(loadFollows, "getFollowingAsync", username, 'blog') + const { pathname } = location_change_action.payload; + const m = pathname.match(/^\/@([a-z0-9\.-]+)/); + if (m && m.length === 2) { + const username = m[1]; + yield fork(fetchFollowCount, username); + yield fork(loadFollows, 'getFollowersAsync', username, 'blog'); + yield fork(loadFollows, 'getFollowingAsync', username, 'blog'); } // `ignore_fetch` case should only trigger on initial page load. No need to call // fetchState immediately after loading fresh state from the server. Details: #593 - const server_location = yield select(state => state.offchain.get('server_location')); - const ignore_fetch = (pathname === server_location && is_initial_state) + const server_location = yield select(state => + state.offchain.get('server_location') + ); + const ignore_fetch = pathname === server_location && is_initial_state; is_initial_state = false; - if(ignore_fetch) return; + if (ignore_fetch) return; let url = `${pathname}`; if (url === '/') url = 'trending'; // Replace /curation-rewards and /author-rewards with /transfers for UserProfile // to resolve data correctly - if (url.indexOf("/curation-rewards") !== -1) url = url.replace("/curation-rewards", "/transfers"); - if (url.indexOf("/author-rewards") !== -1) url = url.replace("/author-rewards", "/transfers"); + if (url.indexOf('/curation-rewards') !== -1) + url = url.replace('/curation-rewards', '/transfers'); + if (url.indexOf('/author-rewards') !== -1) + url = url.replace('/author-rewards', '/transfers'); yield put(appActions.fetchDataBegin()); try { - const state = yield call([api, api.getStateAsync], url) + const state = yield call([api, api.getStateAsync], url); yield put(globalActions.receiveState(state)); } catch (error) { console.error('~~ Saga fetchState error ~~>', url, error); @@ -71,132 +81,190 @@ export function* watchFetchState() { } export function* fetchData(action) { - const {order, author, permlink, accountname} = action.payload; - let {category} = action.payload; - if( !category ) category = ""; + const { order, author, permlink, accountname } = action.payload; + let { category } = action.payload; + if (!category) category = ''; category = category.toLowerCase(); - yield put(globalActions.fetchingData({order, category})); + yield put(globalActions.fetchingData({ order, category })); let call_name, args; if (order === 'trending') { call_name = 'getDiscussionsByTrendingAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; } else if (order === 'trending30') { call_name = 'getDiscussionsByTrending30Async'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; } else if (order === 'promoted') { call_name = 'getDiscussionsByPromotedAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'active' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'active') { call_name = 'getDiscussionsByActiveAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'cashout' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'cashout') { call_name = 'getDiscussionsByCashoutAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'payout' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'payout') { call_name = 'getPostDiscussionsByPayout'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'payout_comments' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'payout_comments') { call_name = 'getCommentDiscussionsByPayout'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'updated' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'updated') { call_name = 'getDiscussionsByActiveAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'created' || order === 'recent' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'created' || order === 'recent') { call_name = 'getDiscussionsByCreatedAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'by_replies' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'by_replies') { call_name = 'getRepliesByLastUpdateAsync'; args = [author, permlink, constants.FETCH_DATA_BATCH_SIZE]; - } else if( order === 'responses' ) { + } else if (order === 'responses') { call_name = 'getDiscussionsByChildrenAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'votes' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'votes') { call_name = 'getDiscussionsByVotesAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'hot' ) { + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'hot') { call_name = 'getDiscussionsByHotAsync'; args = [ - { tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'by_feed' ) { // https://github.com/steemit/steem/issues/249 + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'by_feed') { + // https://github.com/steemit/steem/issues/249 call_name = 'getDiscussionsByFeedAsync'; args = [ - { tag: accountname, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'by_author' ) { + { + tag: accountname, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'by_author') { call_name = 'getDiscussionsByBlogAsync'; args = [ - { tag: accountname, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; - } else if( order === 'by_comments' ) { + { + tag: accountname, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; + } else if (order === 'by_comments') { call_name = 'getDiscussionsByCommentsAsync'; args = [ - { limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; + { + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; } else { call_name = 'getDiscussionsByActiveAsync'; - args = [{ - tag: category, - limit: constants.FETCH_DATA_BATCH_SIZE, - start_author: author, - start_permlink: permlink}]; + args = [ + { + tag: category, + limit: constants.FETCH_DATA_BATCH_SIZE, + start_author: author, + start_permlink: permlink, + }, + ]; } yield put(appActions.fetchDataBegin()); try { const data = yield call([api, api[call_name]], ...args); - yield put(globalActions.receiveData({data, order, category, author, permlink, accountname})); + yield put( + globalActions.receiveData({ + data, + order, + category, + author, + permlink, + accountname, + }) + ); } catch (error) { console.error('~~ Saga fetchData error ~~>', call_name, args, error); yield put(appActions.steemApiError(error.message)); @@ -207,27 +275,30 @@ export function* fetchData(action) { // export function* watchMetaRequests() { // yield* takeLatest('global/REQUEST_META', fetchMeta); // } -export function* fetchMeta({payload: {id, link}}) { +export function* fetchMeta({ payload: { id, link } }) { try { - const metaArray = yield call(() => new Promise((resolve, reject) => { - function reqListener() { - const resp = JSON.parse(this.responseText) - if (resp.error) { - reject(resp.error) - return - } - resolve(resp) - } - const oReq = new XMLHttpRequest() - oReq.addEventListener('load', reqListener) - oReq.open('GET', '/http_metadata/' + link) - oReq.send() - })) - const {title, metaTags} = metaArray - let meta = {title} + const metaArray = yield call( + () => + new Promise((resolve, reject) => { + function reqListener() { + const resp = JSON.parse(this.responseText); + if (resp.error) { + reject(resp.error); + return; + } + resolve(resp); + } + const oReq = new XMLHttpRequest(); + oReq.addEventListener('load', reqListener); + oReq.open('GET', '/http_metadata/' + link); + oReq.send(); + }) + ); + const { title, metaTags } = metaArray; + let meta = { title }; for (let i = 0; i < metaTags.length; i++) { - const [name, content] = metaTags[i] - meta[name] = content + const [name, content] = metaTags[i]; + meta[name] = content; } // http://postimg.org/image/kbefrpbe9/ meta = { @@ -238,13 +309,13 @@ export function* fetchMeta({payload: {id, link}}) { description: meta['twitter:description'], image: meta['twitter:image'], alt: meta['twitter:alt'], + }; + if (!meta.image) { + meta.image = meta['twitter:image:src']; } - if(!meta.image) { - meta.image = meta['twitter:image:src'] - } - yield put(globalActions.receiveMeta({id, meta})) - } catch(error) { - yield put(globalActions.receiveMeta({id, meta: {error}})) + yield put(globalActions.receiveMeta({ id, meta })); + } catch (error) { + yield put(globalActions.receiveMeta({ id, meta: { error } })); } } @@ -257,39 +328,43 @@ export function* watchFetchJsonRequests() { @arg {string} url @arg {object} body (for JSON.stringify) */ -function* fetchJson({payload: {id, url, body, successCallback, skipLoading = false}}) { +function* fetchJson({ + payload: { id, url, body, successCallback, skipLoading = false }, +}) { try { const payload = { method: body ? 'POST' : 'GET', headers: { Accept: 'application/json', - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, - body: body ? JSON.stringify(body) : undefined - } - let result = yield skipLoading ? fetch(url, payload) : call(fetch, url, payload) - result = yield result.json() - if(successCallback) result = successCallback(result) - yield put(globalActions.fetchJsonResult({id, result})) - } catch(error) { - console.error('fetchJson', error) - yield put(globalActions.fetchJsonResult({id, error})) + body: body ? JSON.stringify(body) : undefined, + }; + let result = yield skipLoading + ? fetch(url, payload) + : call(fetch, url, payload); + result = yield result.json(); + if (successCallback) result = successCallback(result); + yield put(globalActions.fetchJsonResult({ id, result })); + } catch (error) { + console.error('fetchJson', error); + yield put(globalActions.fetchJsonResult({ id, error })); } } // Action creators export const actions = { - requestData: (payload) => ({ + requestData: payload => ({ type: REQUEST_DATA, payload, }), - getContent: (payload) => ({ + getContent: payload => ({ type: GET_CONTENT, payload, }), - fetchState: (payload) => ({ + fetchState: payload => ({ type: FETCH_STATE, payload, }), diff --git a/src/app/redux/FetchDataSaga.test.js b/src/app/redux/FetchDataSaga.test.js index 5c82c7535..003d7c778 100644 --- a/src/app/redux/FetchDataSaga.test.js +++ b/src/app/redux/FetchDataSaga.test.js @@ -1,18 +1,18 @@ /*global describe, it, before, beforeEach, after, afterEach */ -import chai, {expect} from 'chai'; +import chai, { expect } from 'chai'; import dirtyChai from 'dirty-chai'; import sinon from 'sinon'; -import {call, put} from 'redux-saga/effects'; -import {fetchState} from './FetchDataSaga'; +import { call, put } from 'redux-saga/effects'; +import { fetchState } from './FetchDataSaga'; chai.use(dirtyChai); const action = { payload: { pathname: '/recent', search: '', - action: 'PUSH' - } + action: 'PUSH', + }, }; describe('sagas', () => { diff --git a/src/app/redux/FollowSaga.js b/src/app/redux/FollowSaga.js index 3c3051af2..6c5e2963f 100644 --- a/src/app/redux/FollowSaga.js +++ b/src/app/redux/FollowSaga.js @@ -1,6 +1,6 @@ -import {fromJS, Map, Set} from 'immutable' -import {call, put, select} from 'redux-saga/effects'; -import {api} from '@steemit/steem-js'; +import { fromJS, Map, Set } from 'immutable'; +import { call, put, select } from 'redux-saga/effects'; +import { api } from '@steemit/steem-js'; import * as globalActions from 'app/redux/GlobalReducer'; @@ -10,88 +10,114 @@ import * as globalActions from 'app/redux/GlobalReducer'; //fetch for follow/following count export function* fetchFollowCount(account) { - const counts = yield call([api, api.getFollowCountAsync], account) - yield put(globalActions.update({ - key: ['follow_count', account], - updater: m => m.mergeDeep({ - follower_count: counts.follower_count, - following_count: counts.following_count}) - })); + const counts = yield call([api, api.getFollowCountAsync], account); + yield put( + globalActions.update({ + key: ['follow_count', account], + updater: m => + m.mergeDeep({ + follower_count: counts.follower_count, + following_count: counts.following_count, + }), + }) + ); } // Test limit with 2 (not 1, infinate looping) export function* loadFollows(method, account, type, force = false) { - if(yield select(state => state.global.getIn(['follow', method, account, type + '_loading']))) { + if ( + yield select(state => + state.global.getIn(['follow', method, account, type + '_loading']) + ) + ) { // console.log('Already loading', method, account, type) - return + return; } - if(!force) { - const hasResult = yield select(state => state.global.hasIn(['follow', method, account, type + '_result'])) - if(hasResult) { + if (!force) { + const hasResult = yield select(state => + state.global.hasIn(['follow', method, account, type + '_result']) + ); + if (hasResult) { // console.log('Already loaded', method, account, type) - return + return; } } - yield put(globalActions.update({ - key: ['follow', method, account], - notSet: Map(), - updater: m => m.set(type + '_loading', true), - })); + yield put( + globalActions.update({ + key: ['follow', method, account], + notSet: Map(), + updater: m => m.set(type + '_loading', true), + }) + ); - yield loadFollowsLoop(method, account, type) + yield loadFollowsLoop(method, account, type); } function* loadFollowsLoop(method, account, type, start = '', limit = 100) { - if(method === "getFollowersAsync") limit = 1000; + if (method === 'getFollowersAsync') limit = 1000; const res = fromJS(yield api[method](account, start, type, limit)); // console.log('res.toJS()', res.toJS()) - let cnt = 0 - let lastAccountName = null + let cnt = 0; + let lastAccountName = null; - yield put(globalActions.update({ - key: ['follow_inprogress', method, account], - notSet: Map(), - updater: (m) => { - m = m.asMutable() - res.forEach((value) => { - cnt += 1; + yield put( + globalActions.update({ + key: ['follow_inprogress', method, account], + notSet: Map(), + updater: m => { + m = m.asMutable(); + res.forEach(value => { + cnt += 1; - const whatList = value.get('what') - const accountNameKey = method === "getFollowingAsync" ? "following" : "follower"; - const accountName = lastAccountName = value.get(accountNameKey) - whatList.forEach((what) => { - //currently this is always true: what === type - m.update(what, Set(), s => s.add(accountName)) - }) - }) - return m.asImmutable() - } - })) + const whatList = value.get('what'); + const accountNameKey = + method === 'getFollowingAsync' + ? 'following' + : 'follower'; + const accountName = (lastAccountName = value.get( + accountNameKey + )); + whatList.forEach(what => { + //currently this is always true: what === type + m.update(what, Set(), s => s.add(accountName)); + }); + }); + return m.asImmutable(); + }, + }) + ); - if(cnt === limit) { + if (cnt === limit) { // This is paging each block of up to limit results - yield call(loadFollowsLoop, method, account, type, lastAccountName) + yield call(loadFollowsLoop, method, account, type, lastAccountName); } else { // This condition happens only once at the very end of the list. // Every account has a different followers and following list for: blog, ignore - yield put(globalActions.update({ - key: [], - updater: (m) => { - m = m.asMutable() + yield put( + globalActions.update({ + key: [], + updater: m => { + m = m.asMutable(); - const result = m.getIn(['follow_inprogress', method, account, type], Set()) - m.deleteIn(['follow_inprogress', method, account, type]) - m.updateIn(['follow', method, account], Map(), mm => mm.merge({ - // Count may be set separately without loading the full xxx_result set - [type + '_count']: result.size, - [type + '_result']: result.sort().reverse(), - [type + '_loading']: false, - })) - return m.asImmutable() - } - })); + const result = m.getIn( + ['follow_inprogress', method, account, type], + Set() + ); + m.deleteIn(['follow_inprogress', method, account, type]); + m.updateIn(['follow', method, account], Map(), mm => + mm.merge({ + // Count may be set separately without loading the full xxx_result set + [type + '_count']: result.size, + [type + '_result']: result.sort().reverse(), + [type + '_loading']: false, + }) + ); + return m.asImmutable(); + }, + }) + ); } } diff --git a/src/app/redux/GlobalReducer.js b/src/app/redux/GlobalReducer.js index 5451aca62..33aef0fe3 100644 --- a/src/app/redux/GlobalReducer.js +++ b/src/app/redux/GlobalReducer.js @@ -1,11 +1,11 @@ -import {Map, Set, List, fromJS, Iterable} from 'immutable'; -import {emptyContent} from 'app/redux/EmptyState'; -import {contentStats} from 'app/utils/StateFunctions'; +import { Map, Set, List, fromJS, Iterable } from 'immutable'; +import { emptyContent } from 'app/redux/EmptyState'; +import { contentStats } from 'app/utils/StateFunctions'; import constants from './constants'; const emptyContentMap = Map(emptyContent); -const defaultState = Map({status: {}}); +const defaultState = Map({ status: {} }); // Action constants const RECEIVE_STATE = 'global/RECEIVE_STATE'; @@ -43,188 +43,269 @@ export default function reducer(state = defaultState, action) { case SET_COLLAPSED: { return state.withMutations(map => { map.updateIn(['content', payload.post], value => { - value.merge(Map({collapsed: payload.collapsed})); + value.merge(Map({ collapsed: payload.collapsed })); }); }); } case RECEIVE_STATE: { - let new_state = fromJS(payload) - if(new_state.has('content')) { + let new_state = fromJS(payload); + if (new_state.has('content')) { const content = new_state.get('content').withMutations(c => { c.forEach((cc, key) => { - cc = emptyContentMap.mergeDeep(cc) - const stats = fromJS(contentStats(cc)) - c.setIn([key, 'stats'], stats) - }) - }) - new_state = new_state.set('content', content) + cc = emptyContentMap.mergeDeep(cc); + const stats = fromJS(contentStats(cc)); + c.setIn([key, 'stats'], stats); + }); + }); + new_state = new_state.set('content', content); } return state.mergeDeep(new_state); } case RECEIVE_ACCOUNT: { const account = fromJS(payload.account, (key, value) => { - if (key === 'witness_votes') return value.toSet() + if (key === 'witness_votes') return value.toSet(); const isIndexed = Iterable.isIndexed(value); return isIndexed ? value.toList() : value.toOrderedMap(); - }) + }); // Merging accounts: A get_state will provide a very full account but a get_accounts will provide a smaller version - return state.updateIn(['accounts', account.get('name')], Map(), a => a.mergeDeep(account)); + return state.updateIn(['accounts', account.get('name')], Map(), a => + a.mergeDeep(account) + ); } case RECEIVE_COMMENT: { - const {author, permlink, parent_author = '', parent_permlink = '', title = '', body} = payload.op; + const { + author, + permlink, + parent_author = '', + parent_permlink = '', + title = '', + body, + } = payload.op; const key = author + '/' + permlink; - let updatedState = state.updateIn(['content', key], Map(emptyContent), r => r.merge({ - author, permlink, parent_author, parent_permlink, - title: title.toString('utf-8'), - body: body.toString('utf-8'), - })); + let updatedState = state.updateIn( + ['content', key], + Map(emptyContent), + r => + r.merge({ + author, + permlink, + parent_author, + parent_permlink, + title: title.toString('utf-8'), + body: body.toString('utf-8'), + }) + ); if (parent_author !== '' && parent_permlink !== '') { const parent_key = parent_author + '/' + parent_permlink; - updatedState = updatedState.updateIn(['content', parent_key, 'replies'], List(), r => r.insert(0, key)); - const children = updatedState.getIn(['content', parent_key, 'replies'], List()).size; - updatedState = updatedState.updateIn(['content', parent_key, 'children'], 0, () => children); + updatedState = updatedState.updateIn( + ['content', parent_key, 'replies'], + List(), + r => r.insert(0, key) + ); + const children = updatedState.getIn( + ['content', parent_key, 'replies'], + List() + ).size; + updatedState = updatedState.updateIn( + ['content', parent_key, 'children'], + 0, + () => children + ); } return updatedState; } case RECEIVE_CONTENT: { - const content = fromJS(payload.content) - const key = content.get('author') + '/' + content.get('permlink') + const content = fromJS(payload.content); + const key = content.get('author') + '/' + content.get('permlink'); return state.updateIn(['content', key], Map(), c => { - c = emptyContentMap.mergeDeep(c) - c = c.delete('active_votes') - c = c.mergeDeep(content) - c = c.set('stats', fromJS(contentStats(c))) - return c + c = emptyContentMap.mergeDeep(c); + c = c.delete('active_votes'); + c = c.mergeDeep(content); + c = c.set('stats', fromJS(contentStats(c))); + return c; }); } case LINK_REPLY: { - const {author, permlink, parent_author = '', parent_permlink = ''} = payload; + const { + author, + permlink, + parent_author = '', + parent_permlink = '', + } = payload; if (parent_author === '' || parent_permlink === '') return state; const key = author + '/' + permlink; const parent_key = parent_author + '/' + parent_permlink; // Add key if not exist - let updatedState = state.updateIn(['content', parent_key, 'replies'], List(), - l => (l.findIndex(i => i === key) === -1 ? l.push(key) : l)) - const children = updatedState.getIn(['content', parent_key, 'replies'], List()).size; - updatedState = updatedState.updateIn(['content', parent_key, 'children'], 0, () => children); + let updatedState = state.updateIn( + ['content', parent_key, 'replies'], + List(), + l => (l.findIndex(i => i === key) === -1 ? l.push(key) : l) + ); + const children = updatedState.getIn( + ['content', parent_key, 'replies'], + List() + ).size; + updatedState = updatedState.updateIn( + ['content', parent_key, 'children'], + 0, + () => children + ); return updatedState; } case UPDATE_ACCOUNT_WITNESS_VOTE: { - const {account, witness, approve} = payload; - return state.updateIn(['accounts', account, 'witness_votes'], Set(), - votes => (approve ? Set(votes).add(witness) : Set(votes).remove(witness))); + const { account, witness, approve } = payload; + return state.updateIn( + ['accounts', account, 'witness_votes'], + Set(), + votes => + approve + ? Set(votes).add(witness) + : Set(votes).remove(witness) + ); } case UPDATE_ACCOUNT_WITNESS_PROXY: { - const {account, proxy} = payload; + const { account, proxy } = payload; return state.setIn(['accounts', account, 'proxy'], proxy); } case DELETE_CONTENT: { - const {author, permlink} = payload; - const key = author + '/' + permlink - const content = state.getIn(['content', key]) - const parent_author = content.get('parent_author') || '' - const parent_permlink = content.get('parent_permlink') || '' - let updatedState = state.deleteIn(['content', key]) + const { author, permlink } = payload; + const key = author + '/' + permlink; + const content = state.getIn(['content', key]); + const parent_author = content.get('parent_author') || ''; + const parent_permlink = content.get('parent_permlink') || ''; + let updatedState = state.deleteIn(['content', key]); if (parent_author !== '' && parent_permlink !== '') { - const parent_key = parent_author + '/' + parent_permlink - updatedState = updatedState.updateIn(['content', parent_key, 'replies'], - List(), r => r.filter(i => i !== key)) + const parent_key = parent_author + '/' + parent_permlink; + updatedState = updatedState.updateIn( + ['content', parent_key, 'replies'], + List(), + r => r.filter(i => i !== key) + ); } return updatedState; } case VOTED: { - const {username, author, permlink, weight} = payload; + const { username, author, permlink, weight } = payload; const key = ['content', author + '/' + permlink, 'active_votes']; let active_votes = state.getIn(key, List()); - const idx = active_votes.findIndex(v => v.get('voter') === username); + const idx = active_votes.findIndex( + v => v.get('voter') === username + ); // steemd flips weight into percent - if(idx === -1) { - active_votes = active_votes.push(Map({voter: username, percent: weight})); + if (idx === -1) { + active_votes = active_votes.push( + Map({ voter: username, percent: weight }) + ); } else { - active_votes = active_votes.set(idx, Map({voter: username, percent: weight})); + active_votes = active_votes.set( + idx, + Map({ voter: username, percent: weight }) + ); } state.setIn(key, active_votes); return state; } case FETCHING_DATA: { - const {order, category} = payload; - const new_state = state.updateIn(['status', category || '', order], () => { - return {fetching: true}; - }); + const { order, category } = payload; + const new_state = state.updateIn( + ['status', category || '', order], + () => { + return { fetching: true }; + } + ); return new_state; } case RECEIVE_DATA: { - const {data, order, category, accountname} = payload; + const { data, order, category, accountname } = payload; let new_state; - if (order === 'by_author' || order === 'by_feed' || order === 'by_comments' || order === 'by_replies') { + if ( + order === 'by_author' || + order === 'by_feed' || + order === 'by_comments' || + order === 'by_replies' + ) { // category is either "blog", "feed", "comments", or "recent_replies" (respectively) -- and all posts are keyed under current profile - const key = ['accounts', accountname, category] + const key = ['accounts', accountname, category]; new_state = state.updateIn(key, List(), list => { return list.withMutations(posts => { data.forEach(value => { - const key2 = `${value.author}/${value.permlink}` + const key2 = `${value.author}/${value.permlink}`; if (!posts.includes(key2)) posts.push(key2); }); }); }); } else { - new_state = state.updateIn(['discussion_idx', category || '', order], list => { - return list.withMutations(posts => { - data.forEach(value => { - const entry = `${value.author}/${value.permlink}`; - if (!posts.includes(entry)) posts.push(entry); + new_state = state.updateIn( + ['discussion_idx', category || '', order], + list => { + return list.withMutations(posts => { + data.forEach(value => { + const entry = `${value.author}/${ + value.permlink + }`; + if (!posts.includes(entry)) posts.push(entry); + }); }); - }); - }); + } + ); } new_state = new_state.updateIn(['content'], content => { return content.withMutations(map => { data.forEach(value => { const key = `${value.author}/${value.permlink}`; - value = fromJS(value) - value = value.set('stats', fromJS(contentStats(value))) + value = fromJS(value); + value = value.set('stats', fromJS(contentStats(value))); map.set(key, value); }); }); }); - new_state = new_state.updateIn(['status', category || '', order], () => { - if (data.length < constants.FETCH_DATA_BATCH_SIZE) { - return {fetching: false, last_fetch: new Date()}; + new_state = new_state.updateIn( + ['status', category || '', order], + () => { + if (data.length < constants.FETCH_DATA_BATCH_SIZE) { + return { fetching: false, last_fetch: new Date() }; + } + return { fetching: false }; } - return {fetching: false}; - }); + ); return new_state; } case RECEIVE_RECENT_POSTS: { - const {data} = payload; - let new_state = state.updateIn(['discussion_idx', '', 'created'], list => { - if (!list) list = List(); - return list.withMutations(posts => { - data.forEach(value => { - const entry = `${value.author}/${value.permlink}`; - if (!posts.includes(entry)) posts.unshift(entry); + const { data } = payload; + let new_state = state.updateIn( + ['discussion_idx', '', 'created'], + list => { + if (!list) list = List(); + return list.withMutations(posts => { + data.forEach(value => { + const entry = `${value.author}/${value.permlink}`; + if (!posts.includes(entry)) posts.unshift(entry); + }); }); - }); - }); + } + ); new_state = new_state.updateIn(['content'], content => { return content.withMutations(map => { data.forEach(value => { const key = `${value.author}/${value.permlink}`; if (!map.has(key)) { - value = fromJS(value) - value = value.set('stats', fromJS(contentStats(value))) + value = fromJS(value); + value = value.set( + 'stats', + fromJS(contentStats(value)) + ); map.set(key, value); } }); @@ -234,33 +315,37 @@ export default function reducer(state = defaultState, action) { } case REQUEST_META: { - const {id, link} = payload; - return state.setIn(['metaLinkData', id], Map({link})); + const { id, link } = payload; + return state.setIn(['metaLinkData', id], Map({ link })); } case RECEIVE_META: { - const {id, meta} = payload; - return state.updateIn(['metaLinkData', id], data => data.merge(meta)); + const { id, meta } = payload; + return state.updateIn(['metaLinkData', id], data => + data.merge(meta) + ); } case SET: { - const {key, value} = payload; + const { key, value } = payload; const key_array = Array.isArray(key) ? key : [key]; return state.setIn(key_array, fromJS(value)); } case REMOVE: { - const key = Array.isArray(payload.key) ? payload.key : [payload.key]; + const key = Array.isArray(payload.key) + ? payload.key + : [payload.key]; return state.removeIn(key); } case UPDATE: { - const {key, notSet = Map(), updater} = payload; + const { key, notSet = Map(), updater } = payload; return state.updateIn(key, notSet, updater); } case SET_META_DATA: { - const {id, meta} = payload; + const { id, meta } = payload; return state.setIn(['metaLinkData', id], fromJS(meta)); } @@ -269,8 +354,10 @@ export default function reducer(state = defaultState, action) { } case CLEAR_META_ELEMENT: { - const {formId, element} = payload; - return state.updateIn(['metaLinkData', formId], data => data.remove(element)); + const { formId, element } = payload; + return state.updateIn(['metaLinkData', formId], data => + data.remove(element) + ); } case FETCH_JSON: { @@ -278,13 +365,15 @@ export default function reducer(state = defaultState, action) { } case FETCH_JSON_RESULT: { - const {id, result, error} = payload; - return state.set(id, fromJS({result, error})); + const { id, result, error } = payload; + return state.set(id, fromJS({ result, error })); } case SHOW_DIALOG: { - const {name, params = {}} = payload; - return state.update('active_dialogs', Map(), d => d.set(name, fromJS({params}))); + const { name, params = {} } = payload; + return state.update('active_dialogs', Map(), d => + d.set(name, fromJS({ params })) + ); } case HIDE_DIALOG: { @@ -298,127 +387,127 @@ export default function reducer(state = defaultState, action) { // Action creators -export const setCollapsed = (payload) => ({ +export const setCollapsed = payload => ({ type: SET_COLLAPSED, payload, }); -export const receiveState = (payload) => ({ +export const receiveState = payload => ({ type: RECEIVE_STATE, payload, }); -export const receiveAccount = (payload) => ({ +export const receiveAccount = payload => ({ type: RECEIVE_ACCOUNT, payload, }); -export const receiveComment = (payload) => ({ +export const receiveComment = payload => ({ type: RECEIVE_COMMENT, payload, }); -export const receiveContent = (payload) => ({ +export const receiveContent = payload => ({ type: RECEIVE_CONTENT, payload, }); -export const linkReply = (payload) => ({ +export const linkReply = payload => ({ type: LINK_REPLY, payload, }); -export const updateAccountWitnessVote = (payload) => ({ +export const updateAccountWitnessVote = payload => ({ type: UPDATE_ACCOUNT_WITNESS_VOTE, payload, }); -export const updateAccountWitnessProxy = (payload) => ({ +export const updateAccountWitnessProxy = payload => ({ type: UPDATE_ACCOUNT_WITNESS_PROXY, payload, }); -export const deleteContent = (payload) => ({ +export const deleteContent = payload => ({ type: DELETE_CONTENT, payload, }); -export const voted = (payload) => ({ +export const voted = payload => ({ type: VOTED, payload, }); -export const fetchingData = (payload) => ({ +export const fetchingData = payload => ({ type: FETCHING_DATA, payload, }); -export const receiveData = (payload) => ({ +export const receiveData = payload => ({ type: RECEIVE_DATA, payload, }); -export const receiveRecentPosts = (payload) => ({ +export const receiveRecentPosts = payload => ({ type: RECEIVE_RECENT_POSTS, payload, }); -export const requestMeta = (payload) => ({ +export const requestMeta = payload => ({ type: REQUEST_META, payload, }); -export const receiveMeta = (payload) => ({ +export const receiveMeta = payload => ({ type: RECEIVE_META, payload, }); -export const set = (payload) => ({ +export const set = payload => ({ type: SET, payload, }); -export const remove = (payload) => ({ +export const remove = payload => ({ type: REMOVE, payload, }); -export const update = (payload) => ({ +export const update = payload => ({ type: UPDATE, payload, }); -export const setMetaData = (payload) => ({ +export const setMetaData = payload => ({ type: SET_META_DATA, payload, }); -export const clearMeta = (payload) => ({ +export const clearMeta = payload => ({ type: CLEAR_META, payload, }); -export const fetchJson = (payload) => ({ +export const fetchJson = payload => ({ type: FETCH_JSON, payload, }); -export const fetchJsonResult = (payload) => ({ +export const fetchJsonResult = payload => ({ type: FETCH_JSON_RESULT, payload, }); -export const showDialog = (payload) => ({ +export const showDialog = payload => ({ type: SHOW_DIALOG, payload, }); -export const hideDialog = (payload) => ({ +export const hideDialog = payload => ({ type: HIDE_DIALOG, payload, }); -export const getState = (payload) => ({ +export const getState = payload => ({ type: GET_STATE, payload, }); diff --git a/src/app/redux/MarketReducer.js b/src/app/redux/MarketReducer.js index bf0f45219..e45f6409c 100644 --- a/src/app/redux/MarketReducer.js +++ b/src/app/redux/MarketReducer.js @@ -1,4 +1,4 @@ -import {Map} from 'immutable'; +import { Map } from 'immutable'; // Action constants const RECEIVE_ORDERBOOK = 'market/RECEIVE_ORDERBOOK'; @@ -9,7 +9,7 @@ const APPEND_TRADE_HISTORY = 'market/APPEND_TRADE_HISTORY'; // Saga-related export const UPDATE_MARKET = 'market/UPDATE_MARKET'; -const defaultState = Map({status: {}}); +const defaultState = Map({ status: {} }); export default function reducer(state = defaultState, action) { const payload = action.payload; @@ -36,32 +36,32 @@ export default function reducer(state = defaultState, action) { } // Action creators -export const receiveOrderbook = (payload) => ({ +export const receiveOrderbook = payload => ({ type: RECEIVE_ORDERBOOK, payload, }); -export const receiveTicker = (payload) => ({ +export const receiveTicker = payload => ({ type: RECEIVE_TICKER, payload, }); -export const receiveOpenOrders = (payload) => ({ +export const receiveOpenOrders = payload => ({ type: RECEIVE_OPEN_ORDERS, payload, }); -export const receiveTradeHistory = (payload) => ({ +export const receiveTradeHistory = payload => ({ type: RECEIVE_TRADE_HISTORY, payload, }); -export const appendTradeHistory = (payload) => ({ +export const appendTradeHistory = payload => ({ type: APPEND_TRADE_HISTORY, payload, }); -export const updateMarket = (payload) => ({ +export const updateMarket = payload => ({ type: UPDATE_MARKET, payload, }); diff --git a/src/app/redux/MarketSaga.js b/src/app/redux/MarketSaga.js index 8c4016d88..99adaedf5 100644 --- a/src/app/redux/MarketSaga.js +++ b/src/app/redux/MarketSaga.js @@ -1,51 +1,61 @@ -import {takeLatest} from 'redux-saga'; -import {call, put} from 'redux-saga/effects'; -import {api} from '@steemit/steem-js'; +import { takeLatest } from 'redux-saga'; +import { call, put } from 'redux-saga/effects'; +import { api } from '@steemit/steem-js'; import * as marketActions from './MarketReducer'; import * as appActions from './AppReducer'; import * as userActions from './UserReducer'; -import {getAccount} from './SagaShared'; +import { getAccount } from './SagaShared'; -export const marketWatches = [watchLocationChange, watchUserLogin, watchMarketUpdate]; +export const marketWatches = [ + watchLocationChange, + watchUserLogin, + watchMarketUpdate, +]; -const wait = ms => ( +const wait = ms => new Promise(resolve => { - setTimeout(() => resolve(), ms) - })) + setTimeout(() => resolve(), ms); + }); -let polling = false -let active_user = null -let last_trade = null +let polling = false; +let active_user = null; +let last_trade = null; export function* fetchMarket(location_change_action) { - const {pathname} = location_change_action.payload; - if (pathname && pathname != "/market") { - polling = false - return + const { pathname } = location_change_action.payload; + if (pathname && pathname != '/market') { + polling = false; + return; } - if(polling == true) return - polling = true - - while(polling) { + if (polling == true) return; + polling = true; + while (polling) { try { const state = yield call([api, api.getOrderBookAsync], 500); yield put(marketActions.receiveOrderbook(state)); let trades; - if(last_trade == null ) { + if (last_trade == null) { trades = yield call([api, api.getRecentTradesAsync], 25); yield put(marketActions.receiveTradeHistory(trades)); } else { - let start = last_trade.toISOString().slice(0, -5) - trades = yield call([api, api.getTradeHistoryAsync], start, "1969-12-31T23:59:59", 1000); - trades = trades.reverse() + let start = last_trade.toISOString().slice(0, -5); + trades = yield call( + [api, api.getTradeHistoryAsync], + start, + '1969-12-31T23:59:59', + 1000 + ); + trades = trades.reverse(); yield put(marketActions.appendTradeHistory(trades)); } - if(trades.length > 0) { - last_trade = new Date((new Date(Date.parse(trades[0]['date']))).getTime() + 1000) + if (trades.length > 0) { + last_trade = new Date( + new Date(Date.parse(trades[0]['date'])).getTime() + 1000 + ); } const state3 = yield call([api, api.getTickerAsync]); @@ -60,7 +70,7 @@ export function* fetchMarket(location_change_action) { } export function* fetchOpenOrders(set_user_action) { - const {username} = set_user_action.payload + const { username } = set_user_action.payload; try { const state = yield call([api, api.getOpenOrdersAsync], username); diff --git a/src/app/redux/OffchainReducer.js b/src/app/redux/OffchainReducer.js index 08fa018f2..3ce35969b 100644 --- a/src/app/redux/OffchainReducer.js +++ b/src/app/redux/OffchainReducer.js @@ -1,7 +1,7 @@ import Immutable from 'immutable'; -import {PropTypes} from 'react'; +import { PropTypes } from 'react'; -const defaultState = Immutable.fromJS({user: {}}); +const defaultState = Immutable.fromJS({ user: {} }); export default function reducer(state = defaultState, action) { if (action.type === 'user/SAVE_LOGIN_CONFIRM') { diff --git a/src/app/redux/PollDataSaga.js b/src/app/redux/PollDataSaga.js index 24295ac35..5f58bc830 100644 --- a/src/app/redux/PollDataSaga.js +++ b/src/app/redux/PollDataSaga.js @@ -1,31 +1,33 @@ import { call, put, select } from 'redux-saga/effects'; -import {api} from '@steemit/steem-js'; +import { api } from '@steemit/steem-js'; import * as appActions from 'app/redux/AppReducer'; -import {getNotifications, webPushRegister} from 'app/utils/ServerApiClient'; +import { getNotifications, webPushRegister } from 'app/utils/ServerApiClient'; import registerServiceWorker from 'app/utils/RegisterServiceWorker'; -const wait = ms => ( +const wait = ms => new Promise(resolve => { - setTimeout(() => resolve(), ms) - }) -) + setTimeout(() => resolve(), ms); + }); let webpush_params = null; function* pollData() { - while(true) { + while (true) { yield call(wait, 20000); - const username = yield select(state => state.user.getIn(['current', 'username'])); + const username = yield select(state => + state.user.getIn(['current', 'username']) + ); if (username) { if (webpush_params === null) { try { webpush_params = yield call(registerServiceWorker); - if (webpush_params) yield call(webPushRegister, username, webpush_params); + if (webpush_params) + yield call(webPushRegister, username, webpush_params); } catch (error) { console.error(error); - webpush_params = {error}; + webpush_params = { error }; } } const nc = yield call(getNotifications, username, webpush_params); diff --git a/src/app/redux/RootReducer.js b/src/app/redux/RootReducer.js index 7b65215a0..19a70ea65 100644 --- a/src/app/redux/RootReducer.js +++ b/src/app/redux/RootReducer.js @@ -1,7 +1,7 @@ -import {Map, fromJS} from 'immutable'; -import {routerReducer} from 'react-router-redux'; -import {combineReducers} from 'redux'; -import {reducer as formReducer} from 'redux-form'; // @deprecated, instead use: app/utils/ReactForm.js +import { Map, fromJS } from 'immutable'; +import { routerReducer } from 'react-router-redux'; +import { combineReducers } from 'redux'; +import { reducer as formReducer } from 'redux-form'; // @deprecated, instead use: app/utils/ReactForm.js import appReducer from './AppReducer'; import globalReducer from './GlobalReducer'; @@ -9,40 +9,40 @@ import marketReducer from './MarketReducer'; import userReducer from './UserReducer'; import transactionReducer from './TransactionReducer'; import offchainReducer from './OffchainReducer'; -import {contentStats} from 'app/utils/StateFunctions'; +import { contentStats } from 'app/utils/StateFunctions'; function initReducer(reducer, type) { return (state, action) => { - if(!state) return reducer(state, action); + if (!state) return reducer(state, action); // @@redux/INIT server and client init if (action.type === '@@redux/INIT' || action.type === '@@INIT') { - if(!(state instanceof Map)) { + if (!(state instanceof Map)) { state = fromJS(state); } - if(type === 'global') { + if (type === 'global') { const content = state.get('content').withMutations(c => { c.forEach((cc, key) => { - if(!c.getIn([key, 'stats'])) { + if (!c.getIn([key, 'stats'])) { // This may have already been set in UniversalRender; if so, then // active_votes were cleared from server response. In this case it // is important to not try to recalculate the stats. (#1040) - c.setIn([key, 'stats'], fromJS(contentStats(cc))) + c.setIn([key, 'stats'], fromJS(contentStats(cc))); } - }) - }) - state = state.set('content', content) + }); + }); + state = state.set('content', content); } - return state + return state; } if (action.type === '@@router/LOCATION_CHANGE' && type === 'global') { - state = state.set('pathname', action.payload.pathname) + state = state.set('pathname', action.payload.pathname); // console.log(action.type, type, action, state.toJS()) } return reducer(state, action); - } + }; } export default combineReducers({ diff --git a/src/app/redux/SagaShared.js b/src/app/redux/SagaShared.js index 04c4885a5..adfa0d320 100644 --- a/src/app/redux/SagaShared.js +++ b/src/app/redux/SagaShared.js @@ -1,41 +1,46 @@ -import {fromJS} from 'immutable' -import {call, put, select} from 'redux-saga/effects'; -import {takeEvery, takeLatest} from 'redux-saga'; +import { fromJS } from 'immutable'; +import { call, put, select } from 'redux-saga/effects'; +import { takeEvery, takeLatest } from 'redux-saga'; import tt from 'counterpart'; -import {api} from '@steemit/steem-js'; +import { api } from '@steemit/steem-js'; -import * as globalActions from './GlobalReducer' +import * as globalActions from './GlobalReducer'; import * as appActions from './AppReducer'; import * as transactionActions from './TransactionReducer'; -import {setUserPreferences} from 'app/utils/ServerApiClient'; +import { setUserPreferences } from 'app/utils/ServerApiClient'; -const wait = ms => ( +const wait = ms => new Promise(resolve => { - setTimeout(() => resolve(), ms) - }) -); + setTimeout(() => resolve(), ms); + }); -export const sharedWatches = [watchGetState, watchTransactionErrors, watchUserSettingsUpdates] +export const sharedWatches = [ + watchGetState, + watchTransactionErrors, + watchUserSettingsUpdates, +]; export function* getAccount(username, force = false) { - let account = yield select(state => state.global.get('accounts').get(username)) + let account = yield select(state => + state.global.get('accounts').get(username) + ); if (force || !account) { - [account] = yield call([api, api.getAccountsAsync], [username]) - if(account) { - account = fromJS(account) - yield put(globalActions.receiveAccount({account})) + [account] = yield call([api, api.getAccountsAsync], [username]); + if (account) { + account = fromJS(account); + yield put(globalActions.receiveAccount({ account })); } } - return account + return account; } export function* watchGetState() { yield* takeEvery(globalActions.GET_STATE, getState); } /** Manual refreshes. The router is in FetchDataSaga. */ -export function* getState({payload: {url}}) { +export function* getState({ payload: { url } }) { try { - const state = yield call([api, api.getStateAsync], url) + const state = yield call([api, api.getStateAsync], url); yield put(globalActions.receiveState(state)); } catch (error) { console.error('~~ Saga getState error ~~>', url, error); @@ -50,22 +55,23 @@ export function* watchTransactionErrors() { function* showTransactionErrorNotification() { const errors = yield select(state => state.transaction.get('errors')); for (const [key, message] of errors) { - yield put(appActions.addNotification({key, message})); + yield put(appActions.addNotification({ key, message })); yield put(transactionActions.deleteError({ key })); } } -export function* getContent({author, permlink, resolve, reject}) { +export function* getContent({ author, permlink, resolve, reject }) { let content; - while(!content) { + while (!content) { content = yield call([api, api.getContentAsync], author, permlink); - if(content["author"] == "") { // retry if content not found. #1870 + if (content['author'] == '') { + // retry if content not found. #1870 content = null; yield call(wait, 3000); } } - yield put(globalActions.receiveContent({content})) + yield put(globalActions.receiveContent({ content })); if (resolve && content) { resolve(content); } else if (reject && !content) { @@ -78,7 +84,7 @@ export function* getContent({author, permlink, resolve, reject}) { * * @param {Object?} params.payload */ -function* saveUserPreferences({payload}) { +function* saveUserPreferences({ payload }) { if (payload) { yield setUserPreferences(payload); } @@ -88,5 +94,12 @@ function* saveUserPreferences({payload}) { } function* watchUserSettingsUpdates() { - yield* takeLatest([appActions.SET_USER_PREFERENCES, appActions.TOGGLE_NIGHTMODE, appActions.TOGGLE_BLOGMODE], saveUserPreferences); + yield* takeLatest( + [ + appActions.SET_USER_PREFERENCES, + appActions.TOGGLE_NIGHTMODE, + appActions.TOGGLE_BLOGMODE, + ], + saveUserPreferences + ); } diff --git a/src/app/redux/TransactionReducer.js b/src/app/redux/TransactionReducer.js index 0d1e70b20..4b0e2e226 100644 --- a/src/app/redux/TransactionReducer.js +++ b/src/app/redux/TransactionReducer.js @@ -1,4 +1,4 @@ -import {fromJS, Map} from 'immutable'; +import { fromJS, Map } from 'immutable'; // Action constants const CONFIRM_OPERATION = 'transaction/CONFIRM_OPERATION'; @@ -15,8 +15,8 @@ export const RECOVER_ACCOUNT = 'transaction/RECOVER_ACCOUNT'; const defaultState = fromJS({ operations: [], - status: { key: '', error: false, busy: false, }, - errors: null + status: { key: '', error: false, busy: false }, + errors: null, }); export default function reducer(state = defaultState, action) { @@ -32,12 +32,16 @@ export default function reducer(state = defaultState, action) { confirmBroadcastOperation: operation, confirmErrorCallback: payload.errorCallback, confirm, - warning + warning, }); } case HIDE_CONFIRM: - return state.merge({show_confirm_modal: false, confirmBroadcastOperation: undefined, confirm: undefined}); + return state.merge({ + show_confirm_modal: false, + confirmBroadcastOperation: undefined, + confirm: undefined, + }); case BROADCAST_OPERATION: // See TransactionSaga.js @@ -50,37 +54,47 @@ export default function reducer(state = defaultState, action) { return state; case ERROR: { - const {operations, error, errorCallback} = payload; + const { operations, error, errorCallback } = payload; let errorStr = error.toString(); let errorKey = 'Transaction broadcast error.'; - for (const [type/*, operation*/] of operations) { + for (const [type /*, operation*/] of operations) { switch (type) { case 'vote': if (/uniqueness constraint/.test(errorStr)) { errorKey = 'You already voted for this post'; - console.error('You already voted for this post.') + console.error('You already voted for this post.'); } break; case 'comment': - if (/You may only post once per minute/.test(errorStr)) { - errorKey = 'You may only post once per minute.' + if ( + /You may only post once per minute/.test(errorStr) + ) { + errorKey = 'You may only post once per minute.'; } else if (errorStr === 'Testing, fake error') errorKey = 'Testing, fake error'; break; case 'transfer': if (/get_balance/.test(errorStr)) { - errorKey = 'Insufficient balance.' + errorKey = 'Insufficient balance.'; } break; case 'withdraw_vesting': - if(/Account registered by another account requires 10x account creation fee worth of Steem Power/.test(errorStr)) - errorKey = 'Account requires 10x the account creation fee in Steem Power (approximately 300 SP) before it can power down.' + if ( + /Account registered by another account requires 10x account creation fee worth of Steem Power/.test( + errorStr + ) + ) + errorKey = + 'Account requires 10x the account creation fee in Steem Power (approximately 300 SP) before it can power down.'; break; default: break; } if (state.hasIn(['TransactionError', type + '_listener'])) { - state = state.setIn(['TransactionError', type], fromJS({key: errorKey, exception: errorStr})) + state = state.setIn( + ['TransactionError', type], + fromJS({ key: errorKey, exception: errorStr }) + ); } else { if (error.message) { // Depends on FC_ASSERT formatting @@ -89,25 +103,35 @@ export default function reducer(state = defaultState, action) { if (err_lines.length > 2) { errorKey = err_lines[1]; const txt = errorKey.split(': '); - if(txt.length && txt[txt.length - 1].trim() !== '') { + if ( + txt.length && + txt[txt.length - 1].trim() !== '' + ) { errorKey = errorStr = txt[txt.length - 1]; } else - errorStr = `Transaction failed: ${err_lines[1]}`; + errorStr = `Transaction failed: ${ + err_lines[1] + }`; } } - if (errorStr.length > 200) errorStr = errorStr.substring(0, 200); + if (errorStr.length > 200) + errorStr = errorStr.substring(0, 200); // Catch for unknown key better error handling if (/unknown key: /.test(errorKey)) { errorKey = "Steem account doesn't exist."; - errorStr = "Transaction failed: Steem account doesn't exist."; + errorStr = + "Transaction failed: Steem account doesn't exist."; } // Catch for invalid active authority if (/Missing Active Authority /.test(errorKey)) { - errorKey = "Not your valid active key."; - errorStr = "Transaction failed: Not your valid active key."; + errorKey = 'Not your valid active key.'; + errorStr = + 'Transaction failed: Not your valid active key.'; } - state = state.update('errors', (errors) => { - return errors ? errors.set(errorKey, errorStr) : Map({[errorKey]: errorStr}); + state = state.update('errors', errors => { + return errors + ? errors.set(errorKey, errorStr) + : Map({ [errorKey]: errorStr }); }); } } @@ -115,7 +139,9 @@ export default function reducer(state = defaultState, action) { if (errorCallback) { errorCallback(errorKey); } else { - throw new Error('PANIC: no callback registered to handle error ' + errorKey); + throw new Error( + 'PANIC: no callback registered to handle error ' + errorKey + ); } return state; @@ -125,10 +151,15 @@ export default function reducer(state = defaultState, action) { return state.deleteIn(['errors', payload.key]); case SET: - return state.setIn(Array.isArray(payload.key) ? payload.key : [payload.key], fromJS(payload.value)) + return state.setIn( + Array.isArray(payload.key) ? payload.key : [payload.key], + fromJS(payload.value) + ); case REMOVE: - return state.removeIn(Array.isArray(payload.key) ? payload.key : [payload.key]); + return state.removeIn( + Array.isArray(payload.key) ? payload.key : [payload.key] + ); default: return state; @@ -136,52 +167,52 @@ export default function reducer(state = defaultState, action) { } // Action creators -export const confirmOperation = (payload) => ({ +export const confirmOperation = payload => ({ type: CONFIRM_OPERATION, payload, }); -export const hideConfirm = (payload) => ({ +export const hideConfirm = payload => ({ type: HIDE_CONFIRM, payload, }); -export const broadcastOperation = (payload) => ({ +export const broadcastOperation = payload => ({ type: BROADCAST_OPERATION, payload, }); -export const updateAuthorities = (payload) => ({ +export const updateAuthorities = payload => ({ type: UPDATE_AUTHORITIES, payload, }); -export const updateMeta = (payload) => ({ +export const updateMeta = payload => ({ type: UPDATE_META, payload, }); -export const error = (payload) => ({ +export const error = payload => ({ type: ERROR, payload, }); -export const deleteError = (payload) => ({ +export const deleteError = payload => ({ type: DELETE_ERROR, payload, }); -export const set = (payload) => ({ +export const set = payload => ({ type: SET, payload, }); -export const remove = (payload) => ({ +export const remove = payload => ({ type: REMOVE, payload, }); -export const recoverAccount = (payload) => ({ +export const recoverAccount = payload => ({ type: RECOVER_ACCOUNT, payload, }); diff --git a/src/app/redux/TransactionSaga.js b/src/app/redux/TransactionSaga.js index ed468b5f4..a062c1b20 100644 --- a/src/app/redux/TransactionSaga.js +++ b/src/app/redux/TransactionSaga.js @@ -1,31 +1,34 @@ -import {takeEvery} from 'redux-saga'; -import {call, put, select} from 'redux-saga/effects'; -import {fromJS, Set, Map} from 'immutable' +import { takeEvery } from 'redux-saga'; +import { call, put, select } from 'redux-saga/effects'; +import { fromJS, Set, Map } from 'immutable'; import tt from 'counterpart'; import getSlug from 'speakingurl'; import base58 from 'bs58'; import secureRandom from 'secure-random'; -import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc'; -import {api, broadcast, auth, memo} from '@steemit/steem-js'; +import { PrivateKey, PublicKey } from '@steemit/steem-js/lib/auth/ecc'; +import { api, broadcast, auth, memo } from '@steemit/steem-js'; -import {getAccount, getContent} from 'app/redux/SagaShared'; -import {findSigningKey} from 'app/redux/AuthSaga' +import { getAccount, getContent } from 'app/redux/SagaShared'; +import { findSigningKey } from 'app/redux/AuthSaga'; import * as appActions from 'app/redux/AppReducer'; import * as globalActions from 'app/redux/GlobalReducer'; import * as transactionActions from 'app/redux/TransactionReducer'; import * as userActions from 'app/redux/UserReducer'; -import {DEBT_TICKER} from 'app/client_config'; -import {serverApiRecordEvent} from 'app/utils/ServerApiClient'; +import { DEBT_TICKER } from 'app/client_config'; +import { serverApiRecordEvent } from 'app/utils/ServerApiClient'; export const transactionWatches = [ watchForBroadcast, watchForUpdateAuthorities, watchForUpdateMeta, watchForRecoverAccount, -] +]; export function* watchForBroadcast() { - yield* takeEvery(transactionActions.BROADCAST_OPERATION, broadcastOperation); + yield* takeEvery( + transactionActions.BROADCAST_OPERATION, + broadcastOperation + ); } export function* watchForUpdateAuthorities() { yield* takeEvery(transactionActions.UPDATE_AUTHORITIES, updateAuthorities); @@ -52,165 +55,275 @@ const hook = { accepted_vote, accepted_account_update, accepted_withdraw_vesting, -} - -function* preBroadcast_transfer({operation}) { - let memoStr = operation.memo - if(memoStr) { - memoStr = toStringUtf8(memoStr) - memoStr = memoStr.trim() - if(/^#/.test(memoStr)) { - const memo_private = yield select( - state => state.user.getIn(['current', 'private_keys', 'memo_private']) - ) - if(!memo_private) throw new Error('Unable to encrypt memo, missing memo private key') - const account = yield call(getAccount, operation.to) - if(!account) throw new Error(`Unknown to account ${operation.to}`) - const memo_key = account.get('memo_key') - memoStr = memo.encode(memo_private, memo_key, memoStr) - operation.memo = memoStr +}; + +function* preBroadcast_transfer({ operation }) { + let memoStr = operation.memo; + if (memoStr) { + memoStr = toStringUtf8(memoStr); + memoStr = memoStr.trim(); + if (/^#/.test(memoStr)) { + const memo_private = yield select(state => + state.user.getIn(['current', 'private_keys', 'memo_private']) + ); + if (!memo_private) + throw new Error( + 'Unable to encrypt memo, missing memo private key' + ); + const account = yield call(getAccount, operation.to); + if (!account) throw new Error(`Unknown to account ${operation.to}`); + const memo_key = account.get('memo_key'); + memoStr = memo.encode(memo_private, memo_key, memoStr); + operation.memo = memoStr; } } - return operation + return operation; } -const toStringUtf8 = o => (o ? Buffer.isBuffer(o) ? o.toString('utf-8') : o.toString() : o) +const toStringUtf8 = o => + o ? (Buffer.isBuffer(o) ? o.toString('utf-8') : o.toString()) : o; -function* preBroadcast_vote({operation, username}) { - if (!operation.voter) operation.voter = username - const {voter, author, permlink, weight} = operation +function* preBroadcast_vote({ operation, username }) { + if (!operation.voter) operation.voter = username; + const { voter, author, permlink, weight } = operation; // give immediate feedback - yield put(globalActions.set({key: `transaction_vote_active_${author}_${permlink}`, value: true})); - yield put(globalActions.voted({username: voter, author, permlink, weight})); - return operation + yield put( + globalActions.set({ + key: `transaction_vote_active_${author}_${permlink}`, + value: true, + }) + ); + yield put( + globalActions.voted({ username: voter, author, permlink, weight }) + ); + return operation; } -function* preBroadcast_account_witness_vote({operation, username}) { - if (!operation.account) operation.account = username - const {account, witness, approve} = operation - yield put(globalActions.updateAccountWitnessVote({account, witness, approve})); - return operation +function* preBroadcast_account_witness_vote({ operation, username }) { + if (!operation.account) operation.account = username; + const { account, witness, approve } = operation; + yield put( + globalActions.updateAccountWitnessVote({ account, witness, approve }) + ); + return operation; } -function* preBroadcast_custom_json({operation}) { - const json = JSON.parse(operation.json) - if(operation.id === 'follow') { +function* preBroadcast_custom_json({ operation }) { + const json = JSON.parse(operation.json); + if (operation.id === 'follow') { try { - if(json[0] === 'follow') { - const {follower, following, what: [action]} = json[1] - yield put(globalActions.update({ - key: ['follow', 'getFollowingAsync', follower], - notSet: Map(), - updater: m => { - //m = m.asMutable() - if(action == null) { - m = m.update('blog_result', Set(), r => r.delete(following)) - m = m.update('ignore_result', Set(), r => r.delete(following)) - } else if(action === 'blog') { - m = m.update('blog_result', Set(), r => r.add(following)) - m = m.update('ignore_result', Set(), r => r.delete(following)) - } else if(action === 'ignore') { - m = m.update('ignore_result', Set(), r => r.add(following)) - m = m.update('blog_result', Set(), r => r.delete(following)) - } - m = m.set('blog_count', m.get('blog_result', Set()).size) - m = m.set('ignore_count', m.get('ignore_result', Set()).size) - return m//.asImmutable() - } - })); + if (json[0] === 'follow') { + const { follower, following, what: [action] } = json[1]; + yield put( + globalActions.update({ + key: ['follow', 'getFollowingAsync', follower], + notSet: Map(), + updater: m => { + //m = m.asMutable() + if (action == null) { + m = m.update('blog_result', Set(), r => + r.delete(following) + ); + m = m.update('ignore_result', Set(), r => + r.delete(following) + ); + } else if (action === 'blog') { + m = m.update('blog_result', Set(), r => + r.add(following) + ); + m = m.update('ignore_result', Set(), r => + r.delete(following) + ); + } else if (action === 'ignore') { + m = m.update('ignore_result', Set(), r => + r.add(following) + ); + m = m.update('blog_result', Set(), r => + r.delete(following) + ); + } + m = m.set( + 'blog_count', + m.get('blog_result', Set()).size + ); + m = m.set( + 'ignore_count', + m.get('ignore_result', Set()).size + ); + return m; //.asImmutable() + }, + }) + ); } - } catch(e) { - console.error('TransactionSaga unrecognized follow custom_json format', operation.json); + } catch (e) { + console.error( + 'TransactionSaga unrecognized follow custom_json format', + operation.json + ); } } - return operation + return operation; } -function* error_account_witness_vote({operation: {account, witness, approve}}) { - yield put(globalActions.updateAccountWitnessVote({account, witness, approve: !approve})); +function* error_account_witness_vote({ + operation: { account, witness, approve }, +}) { + yield put( + globalActions.updateAccountWitnessVote({ + account, + witness, + approve: !approve, + }) + ); } /** Keys, username, and password are not needed for the initial call. This will check the login and may trigger an action to prompt for the password / key. */ -function* broadcastOperation({payload: - {type, operation, confirm, warning, keys, username, password, successCallback, errorCallback, allowPostUnsafe} +function* broadcastOperation({ + payload: { + type, + operation, + confirm, + warning, + keys, + username, + password, + successCallback, + errorCallback, + allowPostUnsafe, + }, }) { - const operationParam = {type, operation, keys, username, password, successCallback, errorCallback, allowPostUnsafe} - - const conf = typeof confirm === 'function' ? confirm() : confirm - if(conf) { - yield put(transactionActions.confirmOperation({ confirm, warning, operation: operationParam, errorCallback })); - return + const operationParam = { + type, + operation, + keys, + username, + password, + successCallback, + errorCallback, + allowPostUnsafe, + }; + + const conf = typeof confirm === 'function' ? confirm() : confirm; + if (conf) { + yield put( + transactionActions.confirmOperation({ + confirm, + warning, + operation: operationParam, + errorCallback, + }) + ); + return; } - const payload = {operations: [[type, operation]], keys, username, successCallback, errorCallback} + const payload = { + operations: [[type, operation]], + keys, + username, + successCallback, + errorCallback, + }; if (!allowPostUnsafe && hasPrivateKeys(payload)) { - const confirm = tt('g.post_key_warning.confirm') - const warning = tt('g.post_key_warning.warning') - const checkbox = tt('g.post_key_warning.checkbox') - operationParam.allowPostUnsafe = true - yield put(transactionActions.confirmOperation({ confirm, warning, checkbox, operation: operationParam, errorCallback })); - return + const confirm = tt('g.post_key_warning.confirm'); + const warning = tt('g.post_key_warning.warning'); + const checkbox = tt('g.post_key_warning.checkbox'); + operationParam.allowPostUnsafe = true; + yield put( + transactionActions.confirmOperation({ + confirm, + warning, + checkbox, + operation: operationParam, + errorCallback, + }) + ); + return; } try { if (!keys || keys.length === 0) { - payload.keys = [] + payload.keys = []; // user may already be logged in, or just enterend a signing passowrd or wif - const signingKey = yield call(findSigningKey, {opType: type, username, password}) - if (signingKey) - payload.keys.push(signingKey) + const signingKey = yield call(findSigningKey, { + opType: type, + username, + password, + }); + if (signingKey) payload.keys.push(signingKey); else { if (!password) { - yield put(userActions.showLogin({ operation: { type, operation, username, successCallback, errorCallback, saveLogin: true } })); - return + yield put( + userActions.showLogin({ + operation: { + type, + operation, + username, + successCallback, + errorCallback, + saveLogin: true, + }, + }) + ); + return; } } } - yield call(broadcastPayload, {payload}) - let eventType = type.replace(/^([a-z])/, g => g.toUpperCase()).replace(/_([a-z])/g, g => g[1].toUpperCase()); - if (eventType === 'Comment' && !operation.parent_author) eventType = 'Post'; - const page = eventType === 'Vote' ? `@${operation.author}/${operation.permlink}` : ''; + yield call(broadcastPayload, { payload }); + let eventType = type + .replace(/^([a-z])/, g => g.toUpperCase()) + .replace(/_([a-z])/g, g => g[1].toUpperCase()); + if (eventType === 'Comment' && !operation.parent_author) + eventType = 'Post'; + const page = + eventType === 'Vote' + ? `@${operation.author}/${operation.permlink}` + : ''; serverApiRecordEvent(eventType, page); - } catch(error) { + } catch (error) { console.error('TransactionSage', error); - if(errorCallback) errorCallback(error.toString()) + if (errorCallback) errorCallback(error.toString()); } } function hasPrivateKeys(payload) { - const blob = JSON.stringify(payload.operations) - let m, re = /P?(5[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50})/g + const blob = JSON.stringify(payload.operations); + let m, + re = /P?(5[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50})/g; while (true) { - m = re.exec(blob) + m = re.exec(blob); if (m) { try { - PrivateKey.fromWif(m[1]) // performs the base58check - return true + PrivateKey.fromWif(m[1]); // performs the base58check + return true; } catch (e) {} } else { - break + break; } } - return false + return false; } -function* broadcastPayload({payload: {operations, keys, username, successCallback, errorCallback}}) { +function* broadcastPayload({ + payload: { operations, keys, username, successCallback, errorCallback }, +}) { // console.log('broadcastPayload') if ($STM_Config.read_only_mode) return; for (const [type] of operations) // see also transaction/ERROR - yield put(transactionActions.remove({ key: ['TransactionError', type] })); + yield put( + transactionActions.remove({ key: ['TransactionError', type] }) + ); { - const newOps = [] + const newOps = []; for (const [type, operation] of operations) { if (hook['preBroadcast_' + type]) { - const op = yield call(hook['preBroadcast_' + type], {operation, username}) - if(Array.isArray(op)) - for(const o of op) - newOps.push(o) - else - newOps.push([type, op]) + const op = yield call(hook['preBroadcast_' + type], { + operation, + username, + }); + if (Array.isArray(op)) for (const o of op) newOps.push(o); + else newOps.push([type, op]); } else { - newOps.push([type, operation]) + newOps.push([type, operation]); } } - operations = newOps + operations = newOps; } // status: broadcasting @@ -218,102 +331,142 @@ function* broadcastPayload({payload: {operations, keys, username, successCallbac for (const [type, operation] of operations) { if (hook['broadcasted_' + type]) { try { - hook['broadcasted_' + type]({operation}) + hook['broadcasted_' + type]({ operation }); } catch (error) { - console.error(error) + console.error(error); } } } - } + }; try { yield new Promise((resolve, reject) => { // Bump transaction (for live UI testing).. Put 0 in now (no effect), // to enable browser's autocomplete and help prevent typos. const env = process.env; - const bump = env.BROWSER ? parseInt(localStorage.getItem('bump') || 0) : 0; - if (env.BROWSER && bump === 1) { // for testing - console.log('TransactionSaga bump(no broadcast) and reject', JSON.stringify(operations, null, 2)) - setTimeout(() => {reject(new Error('Testing, fake error'))}, 2000) - } else if (env.BROWSER && bump === 2) { // also for testing - console.log('TransactionSaga bump(no broadcast) and resolve', JSON.stringify(operations, null, 2)) - setTimeout(() => {resolve(); broadcastedEvent()}, 2000) + const bump = env.BROWSER + ? parseInt(localStorage.getItem('bump') || 0) + : 0; + if (env.BROWSER && bump === 1) { + // for testing + console.log( + 'TransactionSaga bump(no broadcast) and reject', + JSON.stringify(operations, null, 2) + ); + setTimeout(() => { + reject(new Error('Testing, fake error')); + }, 2000); + } else if (env.BROWSER && bump === 2) { + // also for testing + console.log( + 'TransactionSaga bump(no broadcast) and resolve', + JSON.stringify(operations, null, 2) + ); + setTimeout(() => { + resolve(); + broadcastedEvent(); + }, 2000); } else { - broadcast.send({ extensions: [], operations }, keys, (err) => { - if(err) { + broadcast.send({ extensions: [], operations }, keys, err => { + if (err) { console.error(err); - reject(err) + reject(err); } else { - broadcastedEvent() - resolve() + broadcastedEvent(); + resolve(); } - }) + }); } - }) + }); // status: accepted for (const [type, operation] of operations) { if (hook['accepted_' + type]) { try { - yield call(hook['accepted_' + type], {operation}) + yield call(hook['accepted_' + type], { operation }); } catch (error) { - console.error(error) + console.error(error); } } - const config = operation.__config + const config = operation.__config; if (config && config.successMessage) { - yield put(appActions.addNotification({ - key: "trx_" + Date.now(), - message: config.successMessage, - dismissAfter: 5000, - })); + yield put( + appActions.addNotification({ + key: 'trx_' + Date.now(), + message: config.successMessage, + dismissAfter: 5000, + }) + ); } } - if (successCallback) try { successCallback() } catch (error) { console.error(error) } + if (successCallback) + try { + successCallback(); + } catch (error) { + console.error(error); + } } catch (error) { console.error('TransactionSaga\tbroadcastPayload', error); // status: error - yield put(transactionActions.error({ operations, error, errorCallback })); + yield put( + transactionActions.error({ operations, error, errorCallback }) + ); for (const [type, operation] of operations) { if (hook['error_' + type]) { try { - yield call(hook['error_' + type], {operation}) + yield call(hook['error_' + type], { operation }); } catch (error2) { - console.error(error2) + console.error(error2); } } } } } -function* accepted_comment({operation}) { - const {author, permlink} = operation +function* accepted_comment({ operation }) { + const { author, permlink } = operation; // update again with new $$ amount from the steemd node - yield call(getContent, {author, permlink}) + yield call(getContent, { author, permlink }); // receiveComment did the linking already (but that is commented out) yield put(globalActions.linkReply(operation)); // mark the time (can only post 1 per min) // yield put(user.actions.acceptedComment()) } -function* accepted_delete_comment({operation}) { +function* accepted_delete_comment({ operation }) { yield put(globalActions.deleteContent(operation)); } -function* accepted_vote({operation: {author, permlink, weight}}) { - console.log('Vote accepted, weight', weight, 'on', author + '/' + permlink, 'weight'); +function* accepted_vote({ operation: { author, permlink, weight } }) { + console.log( + 'Vote accepted, weight', + weight, + 'on', + author + '/' + permlink, + 'weight' + ); // update again with new $$ amount from the steemd node - yield put(globalActions.remove({ key: `transaction_vote_active_${author}_${permlink}` })); - yield call(getContent, {author, permlink}) + yield put( + globalActions.remove({ + key: `transaction_vote_active_${author}_${permlink}`, + }) + ); + yield call(getContent, { author, permlink }); } -function* accepted_withdraw_vesting({operation}) { - let [account] = yield call([api, api.getAccountsAsync], [operation.account]) - account = fromJS(account) +function* accepted_withdraw_vesting({ operation }) { + let [account] = yield call( + [api, api.getAccountsAsync], + [operation.account] + ); + account = fromJS(account); yield put(globalActions.receiveAccount({ account })); } -function* accepted_account_update({operation}) { - let [account] = yield call([api, api.getAccountsAsync], [operation.account]) - account = fromJS(account) +function* accepted_account_update({ operation }) { + let [account] = yield call( + [api, api.getAccountsAsync], + [operation.account] + ); + account = fromJS(account); yield put(globalActions.receiveAccount({ account })); // bug, fork, etc.. the folowing would be mis-leading @@ -345,125 +498,151 @@ function* accepted_account_update({operation}) { // function* preBroadcast_account_witness_vote({operation, username}) { // } -function* preBroadcast_comment({operation, username}) { - if (!operation.author) operation.author = username - let permlink = operation.permlink - const {author, __config: {originalBody, autoVote, comment_options}} = operation - const {parent_author = '', parent_permlink = operation.category } = operation - const {title} = operation - let {body} = operation - - body = body.trim() +function* preBroadcast_comment({ operation, username }) { + if (!operation.author) operation.author = username; + let permlink = operation.permlink; + const { + author, + __config: { originalBody, autoVote, comment_options }, + } = operation; + const { + parent_author = '', + parent_permlink = operation.category, + } = operation; + const { title } = operation; + let { body } = operation; + + body = body.trim(); // TODO Slightly smaller blockchain comments: if body === json_metadata.steem.link && Object.keys(steem).length > 1 remove steem.link ..This requires an adjust of get_state and the API refresh of the comment to put the steem.link back if Object.keys(steem).length >= 1 - let body2 + let body2; if (originalBody) { - const patch = createPatch(originalBody, body) + const patch = createPatch(originalBody, body); // Putting body into buffer will expand Unicode characters into their true length if (patch && patch.length < new Buffer(body, 'utf-8').length) - body2 = patch + body2 = patch; } - if (!body2) body2 = body - if (!permlink) permlink = yield createPermlink(title, author, parent_author, parent_permlink) - - const md = operation.json_metadata - const json_metadata = typeof md === 'string' ? md : JSON.stringify(md) + if (!body2) body2 = body; + if (!permlink) + permlink = yield createPermlink( + title, + author, + parent_author, + parent_permlink + ); + + const md = operation.json_metadata; + const json_metadata = typeof md === 'string' ? md : JSON.stringify(md); const op = { ...operation, permlink: permlink.toLowerCase(), - parent_author, parent_permlink, json_metadata, + parent_author, + parent_permlink, + json_metadata, title: new Buffer((operation.title || '').trim(), 'utf-8'), body: new Buffer(body2, 'utf-8'), - } + }; - const comment_op = [ - ['comment', op], - ] + const comment_op = [['comment', op]]; // comment_options must come directly after comment - if(comment_options) { + if (comment_options) { const { - max_accepted_payout = ["1000000.000", DEBT_TICKER].join(" "), + max_accepted_payout = ['1000000.000', DEBT_TICKER].join(' '), percent_steem_dollars = 10000, // 10000 === 100% allow_votes = true, allow_curation_rewards = true, - } = comment_options - comment_op.push( - ['comment_options', { + } = comment_options; + comment_op.push([ + 'comment_options', + { author, permlink, max_accepted_payout, percent_steem_dollars, allow_votes, allow_curation_rewards, - extensions: comment_options.extensions ? comment_options.extensions : [] - }] - ) + extensions: comment_options.extensions + ? comment_options.extensions + : [], + }, + ]); } - if(autoVote) { - const vote = {voter: op.author, author: op.author, permlink: op.permlink, weight: 10000} - comment_op.push(['vote', vote]) + if (autoVote) { + const vote = { + voter: op.author, + author: op.author, + permlink: op.permlink, + weight: 10000, + }; + comment_op.push(['vote', vote]); } - return comment_op + return comment_op; } function* createPermlink(title, author, parent_author, parent_permlink) { - let permlink + let permlink; if (title && title.trim() !== '') { - let s = slug(title) - if(s === '') { - s = base58.encode(secureRandom.randomBuffer(4)) + let s = slug(title); + if (s === '') { + s = base58.encode(secureRandom.randomBuffer(4)); } // ensure the permlink(slug) is unique - const slugState = yield call([api, api.getContentAsync], author, s) - let prefix + const slugState = yield call([api, api.getContentAsync], author, s); + let prefix; if (slugState.body !== '') { // make sure slug is unique - prefix = base58.encode(secureRandom.randomBuffer(4)) + '-' + prefix = base58.encode(secureRandom.randomBuffer(4)) + '-'; } else { - prefix = '' + prefix = ''; } - permlink = prefix + s + permlink = prefix + s; } else { // comments: re-parentauthor-parentpermlink-time - const timeStr = new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, '') - parent_permlink = parent_permlink.replace(/(-\d{8}t\d{9}z)/g, '') - permlink = `re-${parent_author}-${parent_permlink}-${timeStr}` + const timeStr = new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, ''); + parent_permlink = parent_permlink.replace(/(-\d{8}t\d{9}z)/g, ''); + permlink = `re-${parent_author}-${parent_permlink}-${timeStr}`; } - if(permlink.length > 255) { + if (permlink.length > 255) { // STEEMIT_MAX_PERMLINK_LENGTH - permlink = permlink.substring(permlink.length - 255, permlink.length) + permlink = permlink.substring(permlink.length - 255, permlink.length); } // only letters numbers and dashes shall survive - permlink = permlink.toLowerCase().replace(/[^a-z0-9-]+/g, '') - return permlink + permlink = permlink.toLowerCase().replace(/[^a-z0-9-]+/g, ''); + return permlink; } -import diff_match_patch from 'diff-match-patch' -const dmp = new diff_match_patch() +import diff_match_patch from 'diff-match-patch'; +const dmp = new diff_match_patch(); function createPatch(text1, text2) { - if (!text1 && text1 === '') return undefined - const patches = dmp.patch_make(text1, text2) - const patch = dmp.patch_toText(patches) - return patch + if (!text1 && text1 === '') return undefined; + const patches = dmp.patch_make(text1, text2); + const patch = dmp.patch_toText(patches); + return patch; } -function* error_custom_json({operation: {id, required_posting_auths}}) { - if(id === 'follow') { - const follower = required_posting_auths[0] - yield put(globalActions.update({ - key: ['follow', 'getFollowingAsync', follower, 'loading'], - updater: () => null - })); +function* error_custom_json({ operation: { id, required_posting_auths } }) { + if (id === 'follow') { + const follower = required_posting_auths[0]; + yield put( + globalActions.update({ + key: ['follow', 'getFollowingAsync', follower, 'loading'], + updater: () => null, + }) + ); } } -function* error_vote({operation: {author, permlink}}) { - yield put(globalActions.remove({ key: `transaction_vote_active_${author}_${permlink}` })); - yield call(getContent, {author, permlink}); // unvote +function* error_vote({ operation: { author, permlink } }) { + yield put( + globalActions.remove({ + key: `transaction_vote_active_${author}_${permlink}`, + }) + ); + yield call(getContent, { author, permlink }); // unvote } // function* error_comment({operation}) { @@ -477,7 +656,7 @@ function* error_vote({operation: {author, permlink}}) { // } function slug(text) { - return getSlug(text.replace(/[<>]/g, ''), {truncate: 128}) + return getSlug(text.replace(/[<>]/g, ''), { truncate: 128 }); //const shorten = txt => { // let t = '' // let words = 0 @@ -508,127 +687,190 @@ function slug(text) { // .toLowerCase() } -const pwPubkey = (name, pw, role) => auth.wifToPublic(auth.toWif(name, pw.trim(), role)) - -function* recoverAccount({payload: {account_to_recover, old_password, new_password, onError, onSuccess}}) { - const [account] = yield call([api, api.getAccountsAsync], [account_to_recover]) - if(!account) { - onError('Unknown account ' + account) - return +const pwPubkey = (name, pw, role) => + auth.wifToPublic(auth.toWif(name, pw.trim(), role)); + +function* recoverAccount({ + payload: { + account_to_recover, + old_password, + new_password, + onError, + onSuccess, + }, +}) { + const [account] = yield call( + [api, api.getAccountsAsync], + [account_to_recover] + ); + if (!account) { + onError('Unknown account ' + account); + return; } - if(auth.isWif(new_password)) { - onError('Your new password should not be a WIF') - return + if (auth.isWif(new_password)) { + onError('Your new password should not be a WIF'); + return; } - if(auth.isPubkey(new_password)) { - onError('Your new password should not be a Public Key') - return + if (auth.isPubkey(new_password)) { + onError('Your new password should not be a Public Key'); + return; } - const oldOwnerPrivate = auth.isWif(old_password) ? old_password : - auth.toWif(account_to_recover, old_password, 'owner') - - const oldOwner = auth.wifToPublic(oldOwnerPrivate) - - const newOwnerPrivate = auth.toWif(account_to_recover, new_password.trim(), 'owner') - const newOwner = auth.wifToPublic(newOwnerPrivate) - const newActive = pwPubkey(account_to_recover, new_password.trim(), 'active') - const newPosting = pwPubkey(account_to_recover, new_password.trim(), 'posting') - const newMemo = pwPubkey(account_to_recover, new_password.trim(), 'memo') - - const new_owner_authority = {weight_threshold: 1, account_auths: [], - key_auths: [[newOwner, 1]]} - - const recent_owner_authority = {weight_threshold: 1, account_auths: [], - key_auths: [[oldOwner, 1]]} + const oldOwnerPrivate = auth.isWif(old_password) + ? old_password + : auth.toWif(account_to_recover, old_password, 'owner'); + + const oldOwner = auth.wifToPublic(oldOwnerPrivate); + + const newOwnerPrivate = auth.toWif( + account_to_recover, + new_password.trim(), + 'owner' + ); + const newOwner = auth.wifToPublic(newOwnerPrivate); + const newActive = pwPubkey( + account_to_recover, + new_password.trim(), + 'active' + ); + const newPosting = pwPubkey( + account_to_recover, + new_password.trim(), + 'posting' + ); + const newMemo = pwPubkey(account_to_recover, new_password.trim(), 'memo'); + + const new_owner_authority = { + weight_threshold: 1, + account_auths: [], + key_auths: [[newOwner, 1]], + }; + + const recent_owner_authority = { + weight_threshold: 1, + account_auths: [], + key_auths: [[oldOwner, 1]], + }; try { - yield broadcast.sendAsync({extensions: [], operations: [ - ['recover_account', { - account_to_recover, - new_owner_authority, - recent_owner_authority, - }] - ]}, [oldOwnerPrivate, newOwnerPrivate]) + yield broadcast.sendAsync( + { + extensions: [], + operations: [ + [ + 'recover_account', + { + account_to_recover, + new_owner_authority, + recent_owner_authority, + }, + ], + ], + }, + [oldOwnerPrivate, newOwnerPrivate] + ); // change password // change password probably requires a separate transaction (single trx has not been tested) - const {json_metadata} = account - yield broadcast.sendAsync({extensions: [], operations: [ - ['account_update', { - account: account.name, - active: {weight_threshold: 1, account_auths: [], key_auths: [[newActive, 1]]}, - posting: {weight_threshold: 1, account_auths: [], key_auths: [[newPosting, 1]]}, - memo_key: newMemo, - json_metadata, - }] - ]}, [newOwnerPrivate]) - if(onSuccess) onSuccess() - } catch(error) { - console.error('Recover account', error) - if(onError) onError(error) + const { json_metadata } = account; + yield broadcast.sendAsync( + { + extensions: [], + operations: [ + [ + 'account_update', + { + account: account.name, + active: { + weight_threshold: 1, + account_auths: [], + key_auths: [[newActive, 1]], + }, + posting: { + weight_threshold: 1, + account_auths: [], + key_auths: [[newPosting, 1]], + }, + memo_key: newMemo, + json_metadata, + }, + ], + ], + }, + [newOwnerPrivate] + ); + if (onSuccess) onSuccess(); + } catch (error) { + console.error('Recover account', error); + if (onError) onError(error); } } /** auths must start with most powerful key: owner for example */ // const twofaAccount = 'steem' -function* updateAuthorities({payload: {accountName, signingKey, auths, twofa, onSuccess, onError}}) { +function* updateAuthorities({ + payload: { accountName, signingKey, auths, twofa, onSuccess, onError }, +}) { // Be sure this account is up-to-date (other required fields are sent in the update) - const [account] = yield call([api, api.getAccountsAsync], [accountName]) + const [account] = yield call([api, api.getAccountsAsync], [accountName]); if (!account) { - onError('Account not found') - return + onError('Account not found'); + return; } // const signingPubkey = signingKey ? signingKey.toPublicKey() : null - const ops2 = {} - let oldPrivate + const ops2 = {}; + let oldPrivate; const addAuth = (authType, oldAuth, newAuth) => { - let oldAuthPubkey, oldPrivateAuth + let oldAuthPubkey, oldPrivateAuth; try { - oldPrivateAuth = PrivateKey.fromWif(oldAuth) - oldAuthPubkey = oldPrivateAuth.toPublic().toString() - } catch(e) { + oldPrivateAuth = PrivateKey.fromWif(oldAuth); + oldAuthPubkey = oldPrivateAuth.toPublic().toString(); + } catch (e) { try { - oldAuthPubkey = PublicKey.fromStringOrThrow(oldAuth).toString() - } catch(e2) { + oldAuthPubkey = PublicKey.fromStringOrThrow(oldAuth).toString(); + } catch (e2) { // } } - if(!oldAuthPubkey) { - if(!oldAuth) { - onError('Missing old key, not sure what to replace') - console.error('Missing old key, not sure what to replace') - return false + if (!oldAuthPubkey) { + if (!oldAuth) { + onError('Missing old key, not sure what to replace'); + console.error('Missing old key, not sure what to replace'); + return false; } - oldPrivateAuth = PrivateKey.fromSeed(accountName + authType + oldAuth) - oldAuthPubkey = oldPrivateAuth.toPublicKey().toString() + oldPrivateAuth = PrivateKey.fromSeed( + accountName + authType + oldAuth + ); + oldAuthPubkey = oldPrivateAuth.toPublicKey().toString(); } - if(authType === 'owner' && !oldPrivate) - oldPrivate = oldPrivateAuth - else if(authType === 'active' && !oldPrivate) - oldPrivate = oldPrivateAuth - else if(authType === 'posting' && !oldPrivate) - oldPrivate = oldPrivateAuth - - let newPrivate, newAuthPubkey + if (authType === 'owner' && !oldPrivate) oldPrivate = oldPrivateAuth; + else if (authType === 'active' && !oldPrivate) + oldPrivate = oldPrivateAuth; + else if (authType === 'posting' && !oldPrivate) + oldPrivate = oldPrivateAuth; + + let newPrivate, newAuthPubkey; try { - newPrivate = PrivateKey.fromWif(newAuth) - newAuthPubkey = newPrivate.toPublicKey().toString() + newPrivate = PrivateKey.fromWif(newAuth); + newAuthPubkey = newPrivate.toPublicKey().toString(); } catch (e) { - newPrivate = PrivateKey.fromSeed(accountName + authType + newAuth) - newAuthPubkey = newPrivate.toPublicKey().toString() + newPrivate = PrivateKey.fromSeed(accountName + authType + newAuth); + newAuthPubkey = newPrivate.toPublicKey().toString(); } // if (oldAuthPubkey === newAuthPubkey) { // onError('This is the same key') // return false // } - let authority + let authority; if (authType === 'memo') { - account.memo_key = newAuthPubkey + account.memo_key = newAuthPubkey; } else { - authority = fromJS(account[authType]).toJS() - authority.key_auths = [] - authority.key_auths.push([newAuthPubkey, authority.weight_threshold]) + authority = fromJS(account[authType]).toJS(); + authority.key_auths = []; + authority.key_auths.push([ + newAuthPubkey, + authority.weight_threshold, + ]); // const key_auths = authority.key_auths // let found // for (let i = 0; i < key_auths.length; i++) { @@ -639,7 +881,7 @@ function* updateAuthorities({payload: {accountName, signingKey, auths, twofa, on // } // } // if (!found) { - // key_auths.push([newAuthPubkey, authority.weight_threshold]) + // key_auths.push([newAuthPubkey, authority.weight_threshold]) // console.log(`Could not find an ${authType} key to update, adding instead`) // } @@ -652,83 +894,97 @@ function* updateAuthorities({payload: {accountName, signingKey, auths, twofa, on // authority.account_auths = account_auths.toJS() // } } - ops2[authType] = authority ? authority : account[authType] - return true - } - for(const auth of auths) - if(!addAuth(auth.authType, auth.oldAuth, auth.newAuth)) - return + ops2[authType] = authority ? authority : account[authType]; + return true; + }; + for (const auth of auths) + if (!addAuth(auth.authType, auth.oldAuth, auth.newAuth)) return; - let key = oldPrivate - if(!key) { + let key = oldPrivate; + if (!key) { try { - key = PrivateKey.fromWif(signingKey) - } catch(e2) { + key = PrivateKey.fromWif(signingKey); + } catch (e2) { // probably updating a memo .. see if we got an active or owner - const auth = (authType) => { - const priv = PrivateKey.fromSeed(accountName + authType + signingKey) - const pubkey = priv.toPublicKey().toString() - const authority = account[authType] - const key_auths = authority.key_auths + const auth = authType => { + const priv = PrivateKey.fromSeed( + accountName + authType + signingKey + ); + const pubkey = priv.toPublicKey().toString(); + const authority = account[authType]; + const key_auths = authority.key_auths; for (let i = 0; i < key_auths.length; i++) { if (key_auths[i][0] === pubkey) { - return priv + return priv; } } - return null - } - key = auth('active') - if(!key) key = auth('owner') + return null; + }; + key = auth('active'); + if (!key) key = auth('owner'); } } if (!key) { - onError(`Incorrect Password`) - throw new Error('Trying to update a memo without a signing key?') + onError(`Incorrect Password`); + throw new Error('Trying to update a memo without a signing key?'); } - const {memo_key, json_metadata} = account + const { memo_key, json_metadata } = account; const payload = { - type: 'account_update', operation: { - account: account.name, ...ops2, - memo_key, json_metadata, - }, keys: [key], + type: 'account_update', + operation: { + account: account.name, + ...ops2, + memo_key, + json_metadata, + }, + keys: [key], successCallback: onSuccess, errorCallback: onError, - } + }; // console.log('sign key.toPublicKey().toString()', key.toPublicKey().toString()) // console.log('payload', payload) - yield call(broadcastOperation, {payload}) + yield call(broadcastOperation, { payload }); } /** auths must start with most powerful key: owner for example */ // const twofaAccount = 'steem' function* updateMeta(params) { // console.log('params', params) - const {meta, account_name, signingKey, onSuccess, onError} = params.payload.operation - console.log('meta', meta) - console.log('account_name', account_name) + const { + meta, + account_name, + signingKey, + onSuccess, + onError, + } = params.payload.operation; + console.log('meta', meta); + console.log('account_name', account_name); // Be sure this account is up-to-date (other required fields are sent in the update) - const [account] = yield call([api, api.getAccountsAsync], [account_name]) + const [account] = yield call([api, api.getAccountsAsync], [account_name]); if (!account) { - onError('Account not found') - return + onError('Account not found'); + return; } if (!signingKey) { - onError(`Incorrect Password`) - throw new Error('Have to pass owner key in order to change meta') + onError(`Incorrect Password`); + throw new Error('Have to pass owner key in order to change meta'); } try { - console.log('account.name', account.name) - const operations = ['update_account_meta', { - account_name: account.name, - json_meta: JSON.stringify(meta), - }] - yield broadcast.sendAsync({extensions: [], operations}, [signingKey]) - if(onSuccess) onSuccess() - // console.log('sign key.toPublicKey().toString()', key.toPublicKey().toString()) - // console.log('payload', payload) - } catch(e) { - console.error('Update meta', e) - if(onError) onError(e) + console.log('account.name', account.name); + const operations = [ + 'update_account_meta', + { + account_name: account.name, + json_meta: JSON.stringify(meta), + }, + ]; + yield broadcast.sendAsync({ extensions: [], operations }, [signingKey]); + if (onSuccess) onSuccess(); + // console.log('sign key.toPublicKey().toString()', key.toPublicKey().toString()) + // console.log('payload', payload) + } catch (e) { + console.error('Update meta', e); + if (onError) onError(e); } } diff --git a/src/app/redux/UserReducer.js b/src/app/redux/UserReducer.js index d0a9f46a8..7aa0d5a8f 100644 --- a/src/app/redux/UserReducer.js +++ b/src/app/redux/UserReducer.js @@ -1,4 +1,4 @@ -import {fromJS} from 'immutable'; +import { fromJS } from 'immutable'; import { DEFAULT_LANGUAGE } from 'app/client_config'; import store from 'store'; @@ -43,7 +43,7 @@ const defaultState = fromJS({ show_promote_post_modal: false, show_signup_modal: false, pub_keys_used: null, - locale: DEFAULT_LANGUAGE + locale: DEFAULT_LANGUAGE, }); if (process.env.BROWSER) { @@ -61,7 +61,11 @@ export default function reducer(state = defaultState, action) { operation = fromJS(payload.operation); loginDefault = fromJS(payload.loginDefault); } - return state.merge({show_login_modal: true, loginBroadcastOperation: operation, loginDefault}); + return state.merge({ + show_login_modal: true, + loginBroadcastOperation: operation, + loginDefault, + }); } case SHOW_TERMS: { @@ -70,11 +74,19 @@ export default function reducer(state = defaultState, action) { operation = fromJS(payload.operation); termsDefault = fromJS(payload.termsDefault); } - return state.merge({show_terms_modal: true, loginBroadcastOperation: operation, termsDefault}); + return state.merge({ + show_terms_modal: true, + loginBroadcastOperation: operation, + termsDefault, + }); } case HIDE_LOGIN: - return state.merge({show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined}); + return state.merge({ + show_login_modal: false, + loginBroadcastOperation: undefined, + loginDefault: undefined, + }); case SAVE_LOGIN_CONFIRM: return state.set('saveLoginConfirm', payload); @@ -86,17 +98,21 @@ export default function reducer(state = defaultState, action) { case REMOVE_HIGH_SECURITY_KEYS: { if (!state.hasIn(['current', 'private_keys'])) return state; let empty = false; - state = state.updateIn(['current', 'private_keys'], private_keys => { - if (!private_keys) return null; - if (private_keys.has('active_private')) console.log('removeHighSecurityKeys'); - private_keys = private_keys.delete('active_private'); - empty = private_keys.size === 0; - return private_keys; - }) + state = state.updateIn( + ['current', 'private_keys'], + private_keys => { + if (!private_keys) return null; + if (private_keys.has('active_private')) + console.log('removeHighSecurityKeys'); + private_keys = private_keys.delete('active_private'); + empty = private_keys.size === 0; + return private_keys; + } + ); if (empty) { // User logged in with Active key then navigates away from the page // LOGOUT - return defaultState.merge({logged_out: true}); + return defaultState.merge({ logged_out: true }); } const username = state.getIn(['current', 'username']); state = state.setIn(['authority', username, 'active'], 'none'); @@ -142,19 +158,40 @@ export default function reducer(state = defaultState, action) { return state; // saga case SET_USER: - if (payload.vesting_shares) payload.vesting_shares = parseFloat(payload.vesting_shares); - if (payload.delegated_vesting_shares) payload.delegated_vesting_shares = parseFloat(payload.delegated_vesting_shares); - if (payload.received_vesting_shares) payload.received_vesting_shares = parseFloat(payload.received_vesting_shares); - return state.mergeDeep({ current: payload, show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined, logged_out: undefined }); + if (payload.vesting_shares) + payload.vesting_shares = parseFloat(payload.vesting_shares); + if (payload.delegated_vesting_shares) + payload.delegated_vesting_shares = parseFloat( + payload.delegated_vesting_shares + ); + if (payload.received_vesting_shares) + payload.received_vesting_shares = parseFloat( + payload.received_vesting_shares + ); + return state.mergeDeep({ + current: payload, + show_login_modal: false, + loginBroadcastOperation: undefined, + loginDefault: undefined, + logged_out: undefined, + }); case CLOSE_LOGIN: - return state.merge({ login_error: undefined, show_login_modal: false, loginBroadcastOperation: undefined, loginDefault: undefined }); + return state.merge({ + login_error: undefined, + show_login_modal: false, + loginBroadcastOperation: undefined, + loginDefault: undefined, + }); case LOGIN_ERROR: - return state.merge({ login_error: payload.error, logged_out: undefined }); + return state.merge({ + login_error: payload.error, + logged_out: undefined, + }); case LOGOUT: - return defaultState.merge({logged_out: true}); + return defaultState.merge({ logged_out: true }); case SHOW_SIGN_UP: return state.set('show_signup_modal', true); @@ -163,7 +200,7 @@ export default function reducer(state = defaultState, action) { return state.set('show_signup_modal', false); case KEYS_ERROR: - return state.merge({ keys_error: payload.error }) + return state.merge({ keys_error: payload.error }); case ACCOUNT_AUTH_LOOKUP: // AuthSaga @@ -171,9 +208,10 @@ export default function reducer(state = defaultState, action) { case SET_AUTHORITY: { // AuthSaga - const {accountName, auth, pub_keys_used} = payload; + const { accountName, auth, pub_keys_used } = payload; state = state.setIn(['authority', accountName], fromJS(auth)); - if (pub_keys_used) state = state.set('pub_keys_used', pub_keys_used); + if (pub_keys_used) + state = state.set('pub_keys_used', pub_keys_used); return state; } @@ -181,7 +219,10 @@ export default function reducer(state = defaultState, action) { return state.set('hide_connection_error_modal', true); case SET: - return state.setIn(Array.isArray(payload.key) ? payload.key : [payload.key], fromJS(payload.value)); + return state.setIn( + Array.isArray(payload.key) ? payload.key : [payload.key], + fromJS(payload.value) + ); default: return state; @@ -189,157 +230,157 @@ export default function reducer(state = defaultState, action) { } // Action creators -export const showLogin = (payload) => ({ +export const showLogin = payload => ({ type: SHOW_LOGIN, payload, }); -export const showTerms = (payload) => ({ +export const showTerms = payload => ({ type: SHOW_TERMS, payload, }); -export const hideLogin = (payload) => ({ +export const hideLogin = payload => ({ type: HIDE_LOGIN, payload, }); -export const saveLoginConfirm = (payload) => ({ +export const saveLoginConfirm = payload => ({ type: SAVE_LOGIN_CONFIRM, payload, }); -export const saveLogin = (payload) => ({ +export const saveLogin = payload => ({ type: SAVE_LOGIN, payload, }); -export const removeHighSecurityKeys = (payload) => ({ +export const removeHighSecurityKeys = payload => ({ type: REMOVE_HIGH_SECURITY_KEYS, payload, }); -export const changeLanguage = (payload) => ({ +export const changeLanguage = payload => ({ type: CHANGE_LANGUAGE, payload, }); -export const showTransfer = (payload) => ({ +export const showTransfer = payload => ({ type: SHOW_TRANSFER, payload, }); -export const hideTransfer = (payload) => ({ +export const hideTransfer = payload => ({ type: HIDE_TRANSFER, payload, }); -export const showPowerdown = (payload) => ({ +export const showPowerdown = payload => ({ type: SHOW_POWERDOWN, payload, }); -export const hidePowerdown = (payload) => ({ +export const hidePowerdown = payload => ({ type: HIDE_POWERDOWN, payload, }); -export const showPromotePost = (payload) => ({ +export const showPromotePost = payload => ({ type: SHOW_PROMOTE_POST, payload, }); -export const hidePromotePost = (payload) => ({ +export const hidePromotePost = payload => ({ type: HIDE_PROMOTE_POST, payload, }); -export const setTransferDefaults = (payload) => ({ +export const setTransferDefaults = payload => ({ type: SET_TRANSFER_DEFAULTS, payload, }); -export const clearTransferDefaults = (payload) => ({ +export const clearTransferDefaults = payload => ({ type: CLEAR_TRANSFER_DEFAULTS, payload, }); -export const setPowerdownDefaults = (payload) => ({ +export const setPowerdownDefaults = payload => ({ type: SET_POWERDOWN_DEFAULTS, payload, }); -export const clearPowerdownDefaults = (payload) => ({ +export const clearPowerdownDefaults = payload => ({ type: CLEAR_POWERDOWN_DEFAULTS, payload, }); -export const usernamePasswordLogin = (payload) => ({ +export const usernamePasswordLogin = payload => ({ type: USERNAME_PASSWORD_LOGIN, payload, }); -export const setUser = (payload) => ({ +export const setUser = payload => ({ type: SET_USER, payload, }); -export const closeLogin = (payload) => ({ +export const closeLogin = payload => ({ type: CLOSE_LOGIN, payload, }); -export const loginError = (payload) => ({ +export const loginError = payload => ({ type: LOGIN_ERROR, payload, }); -export const logout = (payload) => ({ +export const logout = payload => ({ type: LOGOUT, payload, }); -export const showSignUp = (payload) => ({ +export const showSignUp = payload => ({ type: SHOW_SIGN_UP, payload, }); -export const hideSignUp = (payload) => ({ +export const hideSignUp = payload => ({ type: HIDE_SIGN_UP, payload, }); -export const keysError = (payload) => ({ +export const keysError = payload => ({ type: KEYS_ERROR, payload, }); -export const accountAuthLookup = (payload) => ({ +export const accountAuthLookup = payload => ({ type: ACCOUNT_AUTH_LOOKUP, payload, }); -export const setAuthority = (payload) => ({ +export const setAuthority = payload => ({ type: SET_AUTHORITY, payload, }); -export const hideConnectionErrorModal = (payload) => ({ +export const hideConnectionErrorModal = payload => ({ type: HIDE_CONNECTION_ERROR_MODAL, payload, }); -export const set = (payload) => ({ +export const set = payload => ({ type: SET, payload, }); -export const loadSavingsWithdraw = (payload) => ({ +export const loadSavingsWithdraw = payload => ({ type: LOAD_SAVINGS_WITHDRAW, payload, }); -export const uploadImage = (payload) => ({ +export const uploadImage = payload => ({ type: UPLOAD_IMAGE, payload, }); diff --git a/src/app/redux/UserSaga.js b/src/app/redux/UserSaga.js index 412a9150c..61640affa 100644 --- a/src/app/redux/UserSaga.js +++ b/src/app/redux/UserSaga.js @@ -1,23 +1,22 @@ -import {fromJS, Set, List} from 'immutable' -import {takeLatest} from 'redux-saga'; -import {call, put, select, fork} from 'redux-saga/effects'; -import {api} from '@steemit/steem-js'; -import {PrivateKey, Signature, hash} from '@steemit/steem-js/lib/auth/ecc'; - -import {accountAuthLookup} from 'app/redux/AuthSaga' -import {getAccount} from 'app/redux/SagaShared' +import { fromJS, Set, List } from 'immutable'; +import { takeLatest } from 'redux-saga'; +import { call, put, select, fork } from 'redux-saga/effects'; +import { api } from '@steemit/steem-js'; +import { PrivateKey, Signature, hash } from '@steemit/steem-js/lib/auth/ecc'; + +import { accountAuthLookup } from 'app/redux/AuthSaga'; +import { getAccount } from 'app/redux/SagaShared'; import * as userActions from 'app/redux/UserReducer'; -import {browserHistory} from 'react-router' +import { browserHistory } from 'react-router'; import { serverApiLogin, serverApiLogout, serverApiRecordEvent, } from 'app/utils/ServerApiClient'; -import {loadFollows} from 'app/redux/FollowSaga' -import {translate} from 'app/Translator'; +import { loadFollows } from 'app/redux/FollowSaga'; +import { translate } from 'app/Translator'; import DMCAUserList from 'app/utils/DMCAUserList'; - export const userWatches = [ watchRemoveHighSecurityKeys, // keep first to remove keys early when a page change happens loginWatch, @@ -28,15 +27,25 @@ export const userWatches = [ lookupPreviousOwnerAuthorityWatch, watchLoadSavingsWithdraw, uploadImageWatch, -] +]; -const highSecurityPages = [/\/market/, /\/@.+\/(transfers|permissions|password)/, /\/~witnesses/] +const highSecurityPages = [ + /\/market/, + /\/@.+\/(transfers|permissions|password)/, + /\/~witnesses/, +]; function* lookupPreviousOwnerAuthorityWatch() { - yield* takeLatest('user/lookupPreviousOwnerAuthority', lookupPreviousOwnerAuthority); + yield* takeLatest( + 'user/lookupPreviousOwnerAuthority', + lookupPreviousOwnerAuthority + ); } function* loginWatch() { - yield* takeLatest(userActions.USERNAME_PASSWORD_LOGIN, usernamePasswordLogin); + yield* takeLatest( + userActions.USERNAME_PASSWORD_LOGIN, + usernamePasswordLogin + ); } function* saveLoginWatch() { yield* takeLatest(userActions.SAVE_LOGIN, saveLogin_localStorage); @@ -58,37 +67,42 @@ export function* watchRemoveHighSecurityKeys() { } function* loadSavingsWithdraw() { - const username = yield select(state => state.user.getIn(['current', 'username'])) - const to = yield call([api, api.getSavingsWithdrawToAsync], username) - const fro = yield call([api, api.getSavingsWithdrawFromAsync], username) - - const m = {} - for(const v of to) m[v.id] = v - for(const v of fro) m[v.id] = v - - const withdraws = List(fromJS(m).values()) - .sort((a, b) => strCmp(a.get('complete'), b.get('complete'))) - - yield put(userActions.set({ - key: 'savings_withdraws', - value: withdraws, - })); + const username = yield select(state => + state.user.getIn(['current', 'username']) + ); + const to = yield call([api, api.getSavingsWithdrawToAsync], username); + const fro = yield call([api, api.getSavingsWithdrawFromAsync], username); + + const m = {}; + for (const v of to) m[v.id] = v; + for (const v of fro) m[v.id] = v; + + const withdraws = List(fromJS(m).values()).sort((a, b) => + strCmp(a.get('complete'), b.get('complete')) + ); + + yield put( + userActions.set({ + key: 'savings_withdraws', + value: withdraws, + }) + ); } -const strCmp = (a, b) => a > b ? 1 : a < b ? -1 : 0 +const strCmp = (a, b) => (a > b ? 1 : a < b ? -1 : 0); // function* getCurrentAccountWatch() { // // yield* takeLatest('user/SHOW_TRANSFER', getCurrentAccount); // } -function* removeHighSecurityKeys({payload: {pathname}}) { - const highSecurityPage = highSecurityPages.find(p => p.test(pathname)) != null +function* removeHighSecurityKeys({ payload: { pathname } }) { + const highSecurityPage = + highSecurityPages.find(p => p.test(pathname)) != null; // Let the user keep the active key when going from one high security page to another. This helps when // the user logins into the Wallet then the Permissions tab appears (it was hidden). This keeps them // from getting logged out when they click on Permissions (which is really bad because that tab // disappears again). - if(!highSecurityPage) - yield put(userActions.removeHighSecurityKeys()); + if (!highSecurityPage) yield put(userActions.removeHighSecurityKeys()); } /** @@ -98,392 +112,481 @@ function* removeHighSecurityKeys({payload: {pathname}}) { */ function* usernamePasswordLogin(action) { // Sets 'loading' while the login is taking place. The key generation can take a while on slow computers. - yield call(usernamePasswordLogin2, action.payload) - const current = yield select(state => state.user.get('current')) - if(current) { - const username = current.get('username') - yield fork(loadFollows, "getFollowingAsync", username, 'blog') - yield fork(loadFollows, "getFollowingAsync", username, 'ignore') + yield call(usernamePasswordLogin2, action.payload); + const current = yield select(state => state.user.get('current')); + if (current) { + const username = current.get('username'); + yield fork(loadFollows, 'getFollowingAsync', username, 'blog'); + yield fork(loadFollows, 'getFollowingAsync', username, 'ignore'); } } // const isHighSecurityOperations = ['transfer', 'transfer_to_vesting', 'withdraw_vesting', // 'limit_order_create', 'limit_order_cancel', 'account_update', 'account_witness_vote'] - -const clean = (value) => value == null || value === '' || /null|undefined/.test(value) ? undefined : value - -function* usernamePasswordLogin2({username, password, saveLogin, - operationType /*high security*/, afterLoginRedirectToWelcome +const clean = value => + value == null || value === '' || /null|undefined/.test(value) + ? undefined + : value; + +function* usernamePasswordLogin2({ + username, + password, + saveLogin, + operationType /*high security*/, + afterLoginRedirectToWelcome, }) { // login, using saved password let feedURL = false; - let autopost, memoWif, login_owner_pubkey, login_wif_owner_pubkey + let autopost, memoWif, login_owner_pubkey, login_wif_owner_pubkey; if (!username && !password) { - const data = localStorage.getItem('autopost2') - if (data) { // auto-login with a low security key (like a posting key) + const data = localStorage.getItem('autopost2'); + if (data) { + // auto-login with a low security key (like a posting key) autopost = true; // must use simi-colon // The 'password' in this case must be the posting private wif .. See setItme('autopost') - [username, password, memoWif, login_owner_pubkey] = new Buffer(data, 'hex').toString().split('\t'); + [username, password, memoWif, login_owner_pubkey] = new Buffer( + data, + 'hex' + ) + .toString() + .split('\t'); memoWif = clean(memoWif); login_owner_pubkey = clean(login_owner_pubkey); } } // no saved password if (!username || !password) { - const offchain_account = yield select(state => state.offchain.get('account')) - if (offchain_account) serverApiLogout() - return + const offchain_account = yield select(state => + state.offchain.get('account') + ); + if (offchain_account) serverApiLogout(); + return; } - let userProvidedRole // login via: username/owner + let userProvidedRole; // login via: username/owner if (username.indexOf('/') > -1) { // "alice/active" will login only with Alices active key - [username, userProvidedRole] = username.split('/') + [username, userProvidedRole] = username.split('/'); } - const pathname = yield select(state => state.global.get('pathname')) + const pathname = yield select(state => state.global.get('pathname')); const highSecurityLogin = // /owner|active/.test(userProvidedRole) || // isHighSecurityOperations.indexOf(operationType) !== -1 || - highSecurityPages.find(p => p.test(pathname)) != null + highSecurityPages.find(p => p.test(pathname)) != null; - const isRole = (role, fn) => (!userProvidedRole || role === userProvidedRole ? fn() : undefined) + const isRole = (role, fn) => + !userProvidedRole || role === userProvidedRole ? fn() : undefined; - const account = yield call(getAccount, username) + const account = yield call(getAccount, username); if (!account) { yield put(userActions.loginError({ error: 'Username does not exist' })); - return + return; } //dmca user block if (username && DMCAUserList.includes(username)) { - yield put(userActions.loginError({ error: translate('terms_violation') })); - return + yield put( + userActions.loginError({ error: translate('terms_violation') }) + ); + return; } - let private_keys + let private_keys; try { - const private_key = PrivateKey.fromWif(password) - login_wif_owner_pubkey = private_key.toPublicKey().toString() + const private_key = PrivateKey.fromWif(password); + login_wif_owner_pubkey = private_key.toPublicKey().toString(); private_keys = fromJS({ posting_private: isRole('posting', () => private_key), active_private: isRole('active', () => private_key), memo_private: private_key, - }) + }); } catch (e) { // Password (non wif) - login_owner_pubkey = PrivateKey.fromSeed(username + 'owner' + password).toPublicKey().toString() + login_owner_pubkey = PrivateKey.fromSeed(username + 'owner' + password) + .toPublicKey() + .toString(); private_keys = fromJS({ - posting_private: isRole('posting', () => PrivateKey.fromSeed(username + 'posting' + password)), - active_private: isRole('active', () => PrivateKey.fromSeed(username + 'active' + password)), + posting_private: isRole('posting', () => + PrivateKey.fromSeed(username + 'posting' + password) + ), + active_private: isRole('active', () => + PrivateKey.fromSeed(username + 'active' + password) + ), memo_private: PrivateKey.fromSeed(username + 'memo' + password), - }) + }); } if (memoWif) - private_keys = private_keys.set('memo_private', PrivateKey.fromWif(memoWif)) - - yield call(accountAuthLookup, {payload: {account, private_keys, highSecurityLogin, login_owner_pubkey}}) - let authority = yield select(state => state.user.getIn(['authority', username])) - const hasActiveAuth = authority.get('active') === 'full' - if(!highSecurityLogin) { - const accountName = account.get('name') - authority = authority.set('active', 'none') + private_keys = private_keys.set( + 'memo_private', + PrivateKey.fromWif(memoWif) + ); + + yield call(accountAuthLookup, { + payload: { + account, + private_keys, + highSecurityLogin, + login_owner_pubkey, + }, + }); + let authority = yield select(state => + state.user.getIn(['authority', username]) + ); + const hasActiveAuth = authority.get('active') === 'full'; + if (!highSecurityLogin) { + const accountName = account.get('name'); + authority = authority.set('active', 'none'); yield put(userActions.setAuthority({ accountName, auth: authority })); } - const fullAuths = authority.reduce((r, auth, type) => (auth === 'full' ? r.add(type) : r), Set()) + const fullAuths = authority.reduce( + (r, auth, type) => (auth === 'full' ? r.add(type) : r), + Set() + ); if (!fullAuths.size) { - localStorage.removeItem('autopost2') + localStorage.removeItem('autopost2'); const owner_pub_key = account.getIn(['owner', 'key_auths', 0, 0]); - if(login_owner_pubkey === owner_pub_key || login_wif_owner_pubkey === owner_pub_key) { + if ( + login_owner_pubkey === owner_pub_key || + login_wif_owner_pubkey === owner_pub_key + ) { yield put(userActions.loginError({ error: 'owner_login_blocked' })); - } else if(!highSecurityLogin && hasActiveAuth) { - yield put(userActions.loginError({ error: 'active_login_blocked' })); + } else if (!highSecurityLogin && hasActiveAuth) { + yield put( + userActions.loginError({ error: 'active_login_blocked' }) + ); } else { const generated_type = password[0] === 'P' && password.length > 40; - serverApiRecordEvent('login_attempt', JSON.stringify({name: username, login_owner_pubkey, owner_pub_key, generated_type})) + serverApiRecordEvent( + 'login_attempt', + JSON.stringify({ + name: username, + login_owner_pubkey, + owner_pub_key, + generated_type, + }) + ); yield put(userActions.loginError({ error: 'Incorrect Password' })); } - return + return; } if (authority.get('posting') !== 'full') - private_keys = private_keys.remove('posting_private') - - if(!highSecurityLogin || authority.get('active') !== 'full') - private_keys = private_keys.remove('active_private') - - const owner_pubkey = account.getIn(['owner', 'key_auths', 0, 0]) - const active_pubkey = account.getIn(['active', 'key_auths', 0, 0]) - const posting_pubkey = account.getIn(['posting', 'key_auths', 0, 0]) - - if (private_keys.get('memo_private') && - account.get('memo_key') !== private_keys.get('memo_private').toPublicKey().toString() + private_keys = private_keys.remove('posting_private'); + + if (!highSecurityLogin || authority.get('active') !== 'full') + private_keys = private_keys.remove('active_private'); + + const owner_pubkey = account.getIn(['owner', 'key_auths', 0, 0]); + const active_pubkey = account.getIn(['active', 'key_auths', 0, 0]); + const posting_pubkey = account.getIn(['posting', 'key_auths', 0, 0]); + + if ( + private_keys.get('memo_private') && + account.get('memo_key') !== + private_keys + .get('memo_private') + .toPublicKey() + .toString() ) // provided password did not yield memo key - private_keys = private_keys.remove('memo_private') + private_keys = private_keys.remove('memo_private'); - if(!highSecurityLogin) { - if( + if (!highSecurityLogin) { + if ( posting_pubkey === owner_pubkey || posting_pubkey === active_pubkey ) { - yield put(userActions.loginError({ error: 'This login gives owner or active permissions and should not be used here. Please provide a posting only login.' })); - localStorage.removeItem('autopost2') - return + yield put( + userActions.loginError({ + error: + 'This login gives owner or active permissions and should not be used here. Please provide a posting only login.', + }) + ); + localStorage.removeItem('autopost2'); + return; } } - const memo_pubkey = private_keys.has('memo_private') ? - private_keys.get('memo_private').toPublicKey().toString() : null - - if( - memo_pubkey === owner_pubkey || - memo_pubkey === active_pubkey - ) + const memo_pubkey = private_keys.has('memo_private') + ? private_keys + .get('memo_private') + .toPublicKey() + .toString() + : null; + + if (memo_pubkey === owner_pubkey || memo_pubkey === active_pubkey) // Memo key could be saved in local storage.. In RAM it is not purged upon LOCATION_CHANGE - private_keys = private_keys.remove('memo_private') + private_keys = private_keys.remove('memo_private'); // If user is signing operation by operaion and has no saved login, don't save to RAM - if(!operationType || saveLogin) { - if(username) feedURL = '/@' + username + '/feed'; + if (!operationType || saveLogin) { + if (username) feedURL = '/@' + username + '/feed'; // Keep the posting key in RAM but only when not signing an operation. // No operation or the user has checked: Keep me logged in... - yield put(userActions.setUser({ - username, - private_keys, - login_owner_pubkey, - vesting_shares: account.get('vesting_shares'), - received_vesting_shares: account.get('received_vesting_shares'), - delegated_vesting_shares: account.get('delegated_vesting_shares'), - })); + yield put( + userActions.setUser({ + username, + private_keys, + login_owner_pubkey, + vesting_shares: account.get('vesting_shares'), + received_vesting_shares: account.get('received_vesting_shares'), + delegated_vesting_shares: account.get( + 'delegated_vesting_shares' + ), + }) + ); } else { - if(username) feedURL = '/@' + username + '/feed'; - yield put(userActions.setUser({ - username, - vesting_shares: account.get('vesting_shares'), - received_vesting_shares: account.get('received_vesting_shares'), - delegated_vesting_shares: account.get('delegated_vesting_shares'), - })); + if (username) feedURL = '/@' + username + '/feed'; + yield put( + userActions.setUser({ + username, + vesting_shares: account.get('vesting_shares'), + received_vesting_shares: account.get('received_vesting_shares'), + delegated_vesting_shares: account.get( + 'delegated_vesting_shares' + ), + }) + ); } - if (!autopost && saveLogin) - yield put(userActions.saveLogin()); + if (!autopost && saveLogin) yield put(userActions.saveLogin()); try { // const challengeString = yield serverApiLoginChallenge() - const offchainData = yield select(state => state.offchain) - const serverAccount = offchainData.get('account') - const challengeString = offchainData.get('login_challenge') + const offchainData = yield select(state => state.offchain); + const serverAccount = offchainData.get('account'); + const challengeString = offchainData.get('login_challenge'); if (!serverAccount && challengeString) { - const signatures = {} - const challenge = {token: challengeString} - const bufSha = hash.sha256(JSON.stringify(challenge, null, 0)) + const signatures = {}; + const challenge = { token: challengeString }; + const bufSha = hash.sha256(JSON.stringify(challenge, null, 0)); const sign = (role, d) => { - if (!d) return - const sig = Signature.signBufferSha256(bufSha, d) - signatures[role] = sig.toHex() - } - sign('posting', private_keys.get('posting_private')) + if (!d) return; + const sig = Signature.signBufferSha256(bufSha, d); + signatures[role] = sig.toHex(); + }; + sign('posting', private_keys.get('posting_private')); // sign('active', private_keys.get('active_private')) serverApiLogin(username, signatures); } - } catch(error) { + } catch (error) { // Does not need to be fatal console.error('Server Login Error', error); } if (afterLoginRedirectToWelcome) { - browserHistory.push('/welcome') - } else if(feedURL) { - if(document.location.pathname === '/') browserHistory.push(feedURL); + browserHistory.push('/welcome'); + } else if (feedURL) { + if (document.location.pathname === '/') browserHistory.push(feedURL); } } function* saveLogin_localStorage() { if (!process.env.BROWSER) { - console.error('Non-browser environment, skipping localstorage') - return + console.error('Non-browser environment, skipping localstorage'); + return; } - localStorage.removeItem('autopost2') - const [username, private_keys, login_owner_pubkey] = yield select(state => ([ + localStorage.removeItem('autopost2'); + const [username, private_keys, login_owner_pubkey] = yield select(state => [ state.user.getIn(['current', 'username']), state.user.getIn(['current', 'private_keys']), state.user.getIn(['current', 'login_owner_pubkey']), - ])) + ]); if (!username) { - console.error('Not logged in') - return + console.error('Not logged in'); + return; } // Save the lowest security key - const posting_private = private_keys.get('posting_private') + const posting_private = private_keys.get('posting_private'); if (!posting_private) { - console.error('No posting key to save?') - return + console.error('No posting key to save?'); + return; } - const account = yield select(state => state.global.getIn(['accounts', username])) - if(!account) { - console.error('Missing global.accounts[' + username + ']') - return + const account = yield select(state => + state.global.getIn(['accounts', username]) + ); + if (!account) { + console.error('Missing global.accounts[' + username + ']'); + return; } - const postingPubkey = posting_private.toPublicKey().toString() + const postingPubkey = posting_private.toPublicKey().toString(); try { account.getIn(['active', 'key_auths']).forEach(auth => { - if(auth.get(0) === postingPubkey) - throw 'Login will not be saved, posting key is the same as active key' - }) + if (auth.get(0) === postingPubkey) + throw 'Login will not be saved, posting key is the same as active key'; + }); account.getIn(['owner', 'key_auths']).forEach(auth => { - if(auth.get(0) === postingPubkey) - throw 'Login will not be saved, posting key is the same as owner key' - }) - } catch(e) { - console.error(e) - return + if (auth.get(0) === postingPubkey) + throw 'Login will not be saved, posting key is the same as owner key'; + }); + } catch (e) { + console.error(e); + return; } - const memoKey = private_keys.get('memo_private') - const memoWif = memoKey && memoKey.toWif() - const data = new Buffer(`${username}\t${posting_private.toWif()}\t${memoWif || ''}\t${login_owner_pubkey || ''}`).toString('hex') + const memoKey = private_keys.get('memo_private'); + const memoWif = memoKey && memoKey.toWif(); + const data = new Buffer( + `${username}\t${posting_private.toWif()}\t${memoWif || + ''}\t${login_owner_pubkey || ''}` + ).toString('hex'); // autopost is a auto login for a low security key (like the posting key) - localStorage.setItem('autopost2', data) + localStorage.setItem('autopost2', data); } function* logout() { yield put(userActions.saveLoginConfirm(false)); // Just incase it is still showing - if (process.env.BROWSER) - localStorage.removeItem('autopost2') + if (process.env.BROWSER) localStorage.removeItem('autopost2'); serverApiLogout(); } -function* loginError({payload: {/*error*/}}) { +function* loginError({ + payload: { + /*error*/ + }, +}) { serverApiLogout(); } /** If the owner key was changed after the login owner key, this function will find the next owner key history record after the change and store it under user.previous_owner_authority. */ -function* lookupPreviousOwnerAuthority({payload: {}}) { - const current = yield select(state => state.user.getIn(['current'])) - if(!current) return +function* lookupPreviousOwnerAuthority({ payload: {} }) { + const current = yield select(state => state.user.getIn(['current'])); + if (!current) return; - const login_owner_pubkey = current.get('login_owner_pubkey') - if(!login_owner_pubkey) return + const login_owner_pubkey = current.get('login_owner_pubkey'); + if (!login_owner_pubkey) return; - const username = current.get('username') - const key_auths = yield select(state => state.global.getIn(['accounts', username, 'owner', 'key_auths'])) + const username = current.get('username'); + const key_auths = yield select(state => + state.global.getIn(['accounts', username, 'owner', 'key_auths']) + ); if (key_auths && key_auths.find(key => key.get(0) === login_owner_pubkey)) { // console.log('UserSaga ---> Login matches current account owner'); - return + return; } // Owner history since this index was installed July 14 - let owner_history = fromJS(yield call([api, api.getOwnerHistoryAsync], username)) - if(owner_history.count() === 0) return - owner_history = owner_history.sort((b, a) => {//sort decending - const aa = a.get('last_valid_time') - const bb = b.get('last_valid_time') - return aa < bb ? -1 : aa > bb ? 1 : 0 - }) + let owner_history = fromJS( + yield call([api, api.getOwnerHistoryAsync], username) + ); + if (owner_history.count() === 0) return; + owner_history = owner_history.sort((b, a) => { + //sort decending + const aa = a.get('last_valid_time'); + const bb = b.get('last_valid_time'); + return aa < bb ? -1 : aa > bb ? 1 : 0; + }); // console.log('UserSaga ---> owner_history', owner_history.toJS()) const previous_owner_authority = owner_history.find(o => { - const auth = o.get('previous_owner_authority') - const weight_threshold = auth.get('weight_threshold') - const key3 = auth.get('key_auths').find(key2 => key2.get(0) === login_owner_pubkey && key2.get(1) >= weight_threshold) - return key3 ? auth : null - }) - if(!previous_owner_authority) { + const auth = o.get('previous_owner_authority'); + const weight_threshold = auth.get('weight_threshold'); + const key3 = auth + .get('key_auths') + .find( + key2 => + key2.get(0) === login_owner_pubkey && + key2.get(1) >= weight_threshold + ); + return key3 ? auth : null; + }); + if (!previous_owner_authority) { console.log('UserSaga ---> Login owner does not match owner history'); - return + return; } // console.log('UserSage ---> previous_owner_authority', previous_owner_authority.toJS()) - yield put(userActions.setUser({previous_owner_authority})); + yield put(userActions.setUser({ previous_owner_authority })); } function* uploadImageWatch() { yield* takeLatest(userActions.UPLOAD_IMAGE, uploadImage); } -function* uploadImage({payload: {file, dataUrl, filename = 'image.txt', progress}}) { - const _progress = progress +function* uploadImage({ + payload: { file, dataUrl, filename = 'image.txt', progress }, +}) { + const _progress = progress; progress = msg => { // console.log('Upload image progress', msg) - _progress(msg) - } + _progress(msg); + }; - const stateUser = yield select(state => state.user) - const username = stateUser.getIn(['current', 'username']) - const d = stateUser.getIn(['current', 'private_keys', 'posting_private']) - if(!username) { - progress({error: 'Please login first.'}) - return + const stateUser = yield select(state => state.user); + const username = stateUser.getIn(['current', 'username']); + const d = stateUser.getIn(['current', 'private_keys', 'posting_private']); + if (!username) { + progress({ error: 'Please login first.' }); + return; } - if(!d) { - progress({error: 'Login with your posting key'}) - return + if (!d) { + progress({ error: 'Login with your posting key' }); + return; } - if(!file && !dataUrl) { - console.error('uploadImage required: file or dataUrl') - return + if (!file && !dataUrl) { + console.error('uploadImage required: file or dataUrl'); + return; } - let data, dataBs64 - if(file) { + let data, dataBs64; + if (file) { // drag and drop - const reader = new FileReader() + const reader = new FileReader(); data = yield new Promise(resolve => { reader.addEventListener('load', () => { - const result = new Buffer(reader.result, 'binary') - resolve(result) - }) - reader.readAsBinaryString(file) - }) + const result = new Buffer(reader.result, 'binary'); + resolve(result); + }); + reader.readAsBinaryString(file); + }); } else { // recover from preview - const commaIdx = dataUrl.indexOf(',') - dataBs64 = dataUrl.substring(commaIdx + 1) - data = new Buffer(dataBs64, 'base64') + const commaIdx = dataUrl.indexOf(','); + dataBs64 = dataUrl.substring(commaIdx + 1); + data = new Buffer(dataBs64, 'base64'); } // The challenge needs to be prefixed with a constant (both on the server and checked on the client) to make sure the server can't easily make the client sign a transaction doing something else. - const prefix = new Buffer('ImageSigningChallenge') - const bufSha = hash.sha256(Buffer.concat([prefix, data])) + const prefix = new Buffer('ImageSigningChallenge'); + const bufSha = hash.sha256(Buffer.concat([prefix, data])); - const formData = new FormData() - if(file) { - formData.append('file', file) + const formData = new FormData(); + if (file) { + formData.append('file', file); } else { // formData.append('file', file, filename) <- Failed to add filename=xxx to Content-Disposition // Can't easily make this look like a file so this relies on the server supporting: filename and filebinary - formData.append('filename', filename) - formData.append('filebase64', dataBs64) + formData.append('filename', filename); + formData.append('filebase64', dataBs64); } - const sig = Signature.signBufferSha256(bufSha, d) - const postUrl = `${$STM_Config.upload_image}/${username}/${sig.toHex()}` - - const xhr = new XMLHttpRequest() - xhr.open('POST', postUrl) - xhr.onload = function () { - console.log(xhr.status, xhr.responseText) - const res = JSON.parse(xhr.responseText) - const {error} = res - if(error) { - progress({error: 'Error: ' + error}) - return + const sig = Signature.signBufferSha256(bufSha, d); + const postUrl = `${$STM_Config.upload_image}/${username}/${sig.toHex()}`; + + const xhr = new XMLHttpRequest(); + xhr.open('POST', postUrl); + xhr.onload = function() { + console.log(xhr.status, xhr.responseText); + const res = JSON.parse(xhr.responseText); + const { error } = res; + if (error) { + progress({ error: 'Error: ' + error }); + return; } - const {url} = res - progress({url}) - } - xhr.onerror = function (error) { - console.error(filename, error) - progress({error: 'Unable to contact the server.'}) - } - xhr.upload.onprogress = function (event) { + const { url } = res; + progress({ url }); + }; + xhr.onerror = function(error) { + console.error(filename, error); + progress({ error: 'Unable to contact the server.' }); + }; + xhr.upload.onprogress = function(event) { if (event.lengthComputable) { - const percent = Math.round((event.loaded / event.total) * 100) - progress({message: `Uploading ${percent}%`}) + const percent = Math.round(event.loaded / event.total * 100); + progress({ message: `Uploading ${percent}%` }); // console.log('Upload', percent) } - } - xhr.send(formData) + }; + xhr.send(formData); } - // function* getCurrentAccount() { // const current = yield select(state => state.user.get('current')) // if (!current) return diff --git a/src/app/redux/tests/AppReducer.test.js b/src/app/redux/tests/AppReducer.test.js index c20ae36bd..a23377c63 100644 --- a/src/app/redux/tests/AppReducer.test.js +++ b/src/app/redux/tests/AppReducer.test.js @@ -1,8 +1,8 @@ /*global describe, it, before, beforeEach, after, afterEach */ -import chai, {expect} from 'chai'; +import chai, { expect } from 'chai'; import dirtyChai from 'dirty-chai'; import chaiImmutable from 'chai-immutable'; -import {Map} from 'immutable'; +import { Map } from 'immutable'; import reducer from '../AppReducer'; chai.use(dirtyChai); chai.use(chaiImmutable); @@ -10,28 +10,25 @@ chai.use(chaiImmutable); const defaultState = Map({ effects: Map({}), loading: false, - error: '' + error: '', }); const effectTriggered = { type: 'EFFECT_TRIGGERED', effectId: 1, effect: { - CALL: true - } + CALL: true, + }, }; const effectResolved = { type: 'EFFECT_RESOLVED', - effectId: '1' + effectId: '1', }; - describe('AppReducer', () => { it('should return default state', () => { - expect( - reducer(undefined, {}) - ).to.equal(defaultState); + expect(reducer(undefined, {})).to.equal(defaultState); }); it('triggered effect should be added to effects and turn on loading', () => { @@ -42,9 +39,9 @@ describe('AppReducer', () => { it('resolved effect should be added to effects and turn on loading', () => { const triggeredState = Map({ - effects: Map({['1']: Date.now()}), + effects: Map({ ['1']: Date.now() }), loading: true, - error: '' + error: '', }); const state = reducer(triggeredState, effectResolved); expect(state.get('effects').size).to.equal(0); diff --git a/src/app/redux/tests/global.json b/src/app/redux/tests/global.json index f25e83672..ff477de1d 100644 --- a/src/app/redux/tests/global.json +++ b/src/app/redux/tests/global.json @@ -1,82 +1,82 @@ { - "pathname": "trending", - "props": { - "id": "2.0.0", - "head_block_number": 38897, - "head_block_id": "000097f1d40a280e4758128e7b57d60cee9df36d", - "time": "2016-03-24T15:39:06", - "current_witness": "mottler-5", - "total_pow": "18446744048449238345", - "num_pow_witnesses": 79, - "virtual_supply": "156738.000 STEEM", - "current_supply": "156738.000 STEEM", - "confidential_supply": "0.000 STEEM", - "current_sbd_supply": "0.000 SBD", - "confidential_sbd_supply": "0.000 SBD", - "total_vesting_fund_steem": "274.000 STEEM", - "total_vesting_shares": "274.000000 VESTS", - "total_reward_fund_steem": "77794.000 STEEM", - "total_reward_shares2": "0", - "sbd_interest_rate": 1000, - "average_block_size": 117, - "maximum_block_size": 131072, - "current_aslot": 47582, - "recent_slots_filled": "329648522672200722443889453235959593423", - "last_irreversible_block_num": 38876, - "max_virtual_bandwidth": "2203586534107862357", - "current_reserve_ratio": 1945 - }, - "tag_idx": { - "trending": [], - "active": [], - "recent": [], - "best": [] - }, - "categories": {}, - "content": {}, - "accounts": {}, - "pow_queue": [], - "witnesses": { - "id": "2.7.0", - "current_virtual_time": "0", - "next_shuffle_block_num": 38913, - "current_shuffled_witnesses": [ - "mottler-6", - "alice1", - "mottler-4", - "faddy3", - "dumpthisjunk", - "dealthandtaxes", - "lambda", - "cloop3", - "mr11acdee3", - "dumpcoin", - "nxt", - "devshuster", - "nxt1", - "mottler-7", - "failcoin1", - "mottler-1", - "smooth", - "mottler-5", - "blizzt", - "rurikovich", - "woot" - ], - "median_props": { - "account_creation_fee": "100.000 STEEM", - "maximum_block_size": 131072, - "sbd_interest_rate": 1000 + "pathname": "trending", + "props": { + "id": "2.0.0", + "head_block_number": 38897, + "head_block_id": "000097f1d40a280e4758128e7b57d60cee9df36d", + "time": "2016-03-24T15:39:06", + "current_witness": "mottler-5", + "total_pow": "18446744048449238345", + "num_pow_witnesses": 79, + "virtual_supply": "156738.000 STEEM", + "current_supply": "156738.000 STEEM", + "confidential_supply": "0.000 STEEM", + "current_sbd_supply": "0.000 SBD", + "confidential_sbd_supply": "0.000 SBD", + "total_vesting_fund_steem": "274.000 STEEM", + "total_vesting_shares": "274.000000 VESTS", + "total_reward_fund_steem": "77794.000 STEEM", + "total_reward_shares2": "0", + "sbd_interest_rate": 1000, + "average_block_size": 117, + "maximum_block_size": 131072, + "current_aslot": 47582, + "recent_slots_filled": "329648522672200722443889453235959593423", + "last_irreversible_block_num": 38876, + "max_virtual_bandwidth": "2203586534107862357", + "current_reserve_ratio": 1945 + }, + "tag_idx": { + "trending": [], + "active": [], + "recent": [], + "best": [] + }, + "categories": {}, + "content": {}, + "accounts": {}, + "pow_queue": [], + "witnesses": { + "id": "2.7.0", + "current_virtual_time": "0", + "next_shuffle_block_num": 38913, + "current_shuffled_witnesses": [ + "mottler-6", + "alice1", + "mottler-4", + "faddy3", + "dumpthisjunk", + "dealthandtaxes", + "lambda", + "cloop3", + "mr11acdee3", + "dumpcoin", + "nxt", + "devshuster", + "nxt1", + "mottler-7", + "failcoin1", + "mottler-1", + "smooth", + "mottler-5", + "blizzt", + "rurikovich", + "woot" + ], + "median_props": { + "account_creation_fee": "100.000 STEEM", + "maximum_block_size": 131072, + "sbd_interest_rate": 1000 + } + }, + "discussion_idx": { + "": { + "category": "", + "trending": [], + "recent": [], + "active": [], + "maturing": [], + "best": [] + } } - }, - "discussion_idx": { - "": { - "category": "", - "trending": [], - "recent": [], - "active": [], - "maturing": [], - "best": [] - } - } } diff --git a/src/app/redux/tests/global.test.js b/src/app/redux/tests/global.test.js index 05daa91c8..b0b13c091 100644 --- a/src/app/redux/tests/global.test.js +++ b/src/app/redux/tests/global.test.js @@ -1,23 +1,21 @@ /*global describe, it, before, beforeEach, after, afterEach */ -import chai, {expect} from 'chai'; +import chai, { expect } from 'chai'; import chaiImmutable from 'chai-immutable'; -import Immutable, {Map} from 'immutable'; +import Immutable, { Map } from 'immutable'; import reducer, * as globalActions from '../GlobalReducer'; chai.use(chaiImmutable); describe('global reducer', () => { it('should return empty state', () => { - expect( - reducer(undefined, {}) - ).to.equal(Map({})); + expect(reducer(undefined, {})).to.equal(Map({})); }); it('should apply new global state', () => { const state = Immutable.fromJS(require('./global.json')); //const action = {type: 'global/RECEIVE_STATE', payload: state}; - expect( - reducer(undefined, globalActions.receiveState(state)) - ).to.equal(state); + expect(reducer(undefined, globalActions.receiveState(state))).to.equal( + state + ); }); }); diff --git a/src/app/utils/Accessors.js b/src/app/utils/Accessors.js index dd56b75e4..b650f8f07 100644 --- a/src/app/utils/Accessors.js +++ b/src/app/utils/Accessors.js @@ -1,11 +1,17 @@ export function immutableAccessor(obj, ...keys) { - if (!obj) return {} + if (!obj) return {}; if (keys.length === 1) return obj.get(keys[0]); - return keys.reduce((res, key) => {res[key] = obj.get(key); return res;}, {}); + return keys.reduce((res, key) => { + res[key] = obj.get(key); + return res; + }, {}); } export function objAccessor(obj, ...keys) { - if (!obj) return {} + if (!obj) return {}; if (keys.length === 1) return obj[keys[0]]; - return keys.reduce((res, key) => {res[key] = obj[key]; return res;}, {}); + return keys.reduce((res, key) => { + res[key] = obj[key]; + return res; + }, {}); } diff --git a/src/app/utils/AppPropTypes.js b/src/app/utils/AppPropTypes.js index b74c1c057..58a91eb3c 100644 --- a/src/app/utils/AppPropTypes.js +++ b/src/app/utils/AppPropTypes.js @@ -1,8 +1,8 @@ -import {PropTypes} from 'react'; +import { PropTypes } from 'react'; const Children = PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), - PropTypes.node + PropTypes.node, ]); -export default {Children}; +export default { Children }; diff --git a/src/app/utils/BadActorList.js b/src/app/utils/BadActorList.js index e6ca493df..a76db331e 100644 --- a/src/app/utils/BadActorList.js +++ b/src/app/utils/BadActorList.js @@ -112,6 +112,8 @@ bloctrades blocktradess blocktrade blocktraders -`.trim().split('\n'); +` + .trim() + .split('\n'); export default list; diff --git a/src/app/utils/BrowserTests.js b/src/app/utils/BrowserTests.js index 876673553..a41f22c8b 100644 --- a/src/app/utils/BrowserTests.js +++ b/src/app/utils/BrowserTests.js @@ -1,44 +1,46 @@ -import assert from 'assert' -import {serverApiRecordEvent} from 'app/utils/ServerApiClient' -import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc' -import {config} from '@steemit/steem-js'; +import assert from 'assert'; +import { serverApiRecordEvent } from 'app/utils/ServerApiClient'; +import { PrivateKey, PublicKey } from '@steemit/steem-js/lib/auth/ecc'; +import { config } from '@steemit/steem-js'; -export const browserTests = {} +export const browserTests = {}; export default function runTests() { - let rpt = '' - let pass = true + let rpt = ''; + let pass = true; function it(name, fn) { - console.log('Testing', name) - rpt += 'Testing ' + name + '\n' + console.log('Testing', name); + rpt += 'Testing ' + name + '\n'; try { - fn() - } catch(error) { - console.error(error) - pass = false - rpt += error.stack + '\n\n' - serverApiRecordEvent('client_error', error) + fn(); + } catch (error) { + console.error(error); + pass = false; + rpt += error.stack + '\n\n'; + serverApiRecordEvent('client_error', error); } } - let private_key, public_key - const wif = '5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw' - const pubkey = config.get('address_prefix') +'8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA' + let private_key, public_key; + const wif = '5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw'; + const pubkey = + config.get('address_prefix') + + '8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA'; it('create private key', () => { - private_key = PrivateKey.fromSeed('1') - assert.equal(private_key.toWif(), wif) - }) + private_key = PrivateKey.fromSeed('1'); + assert.equal(private_key.toWif(), wif); + }); it('supports WIF format', () => { - assert(PrivateKey.fromWif(wif)) - }) + assert(PrivateKey.fromWif(wif)); + }); it('finds public from private key', () => { - public_key = private_key.toPublicKey() + public_key = private_key.toPublicKey(); // substring match ignore prefix - assert.equal(public_key.toString(), pubkey, 'Public key did not match') - }) + assert.equal(public_key.toString(), pubkey, 'Public key did not match'); + }); it('parses public key', () => { - assert(PublicKey.fromString(public_key.toString())) - }) - if(!pass) return rpt + assert(PublicKey.fromString(public_key.toString())); + }); + if (!pass) return rpt; } diff --git a/src/app/utils/ChainValidation.js b/src/app/utils/ChainValidation.js index 19a01e21a..e48bd0753 100644 --- a/src/app/utils/ChainValidation.js +++ b/src/app/utils/ChainValidation.js @@ -1,7 +1,7 @@ import tt from 'counterpart'; import BadActorList from 'app/utils/BadActorList'; import VerifiedExchangeList from 'app/utils/VerifiedExchangeList'; -import {PrivateKey, PublicKey} from '@steemit/steem-js/lib/auth/ecc'; +import { PrivateKey, PublicKey } from '@steemit/steem-js/lib/auth/ecc'; export function validate_account_name(value, memo) { let i, label, len, length, ref, suffix; @@ -24,7 +24,7 @@ export function validate_account_name(value, memo) { return 'Use caution sending to this account. Please double check your spelling for possible phishing. '; } if (VerifiedExchangeList.includes(value) && !memo) { - return tt('chainvalidation_js.verified_exchange_no_memo') + return tt('chainvalidation_js.verified_exchange_no_memo'); } ref = value.split('.'); for (i = 0, len = ref.length; i < len; i++) { @@ -33,10 +33,15 @@ export function validate_account_name(value, memo) { return suffix + tt('chainvalidation_js.start_with_a_letter'); } if (!/^[a-z0-9-]*$/.test(label)) { - return suffix + tt('chainvalidation_js.have_only_letters_digits_or_dashes'); + return ( + suffix + + tt('chainvalidation_js.have_only_letters_digits_or_dashes') + ); } if (/--/.test(label)) { - return suffix + tt('chainvalidation_js.have_only_one_dash_in_a_row'); + return ( + suffix + tt('chainvalidation_js.have_only_one_dash_in_a_row') + ); } if (!/[a-z0-9]$/.test(label)) { return suffix + tt('chainvalidation_js.end_with_a_letter_or_digit'); @@ -50,16 +55,22 @@ export function validate_account_name(value, memo) { export function validate_memo_field(value, username, memokey) { let suffix; - value = value.split(' ').filter(v=>v!=''); + value = value.split(' ').filter(v => v != ''); for (var w in value) { if (PrivateKey.isWif(value[w])) { - return suffix = 'Do not use private keys in memos. '; + return (suffix = 'Do not use private keys in memos. '); } - if (memokey === PrivateKey.fromSeed(username + 'memo' + value[w]).toPublicKey().toString()) { - return suffix = 'Do not use passwords in memos. '; + if ( + memokey === + PrivateKey.fromSeed(username + 'memo' + value[w]) + .toPublicKey() + .toString() + ) { + return (suffix = 'Do not use passwords in memos. '); } if (/5[HJK]\w{40,45}/i.test(value[w])) { - return suffix = 'Please do not include what appears to be a private key or password. ' + return (suffix = + 'Please do not include what appears to be a private key or password. '); } } return null; diff --git a/src/app/utils/ComponentFormatters.jsx b/src/app/utils/ComponentFormatters.jsx index d5ee9912f..18987c469 100644 --- a/src/app/utils/ComponentFormatters.jsx +++ b/src/app/utils/ComponentFormatters.jsx @@ -1,6 +1,10 @@ -import React from 'react' +import React from 'react'; -export const authorNameAndRep = (author, authorRepLog10) => - {author} - {authorRepLog10 != null && ({authorRepLog10})} - \ No newline at end of file +export const authorNameAndRep = (author, authorRepLog10) => ( + + {author} + {authorRepLog10 != null && ( + ({authorRepLog10}) + )} + +); diff --git a/src/app/utils/ConsoleExports.js b/src/app/utils/ConsoleExports.js index 0d3387b07..25fbf5864 100644 --- a/src/app/utils/ConsoleExports.js +++ b/src/app/utils/ConsoleExports.js @@ -1,55 +1,63 @@ -import {PrivateKey, PublicKey, Aes, key_utils} from '@steemit/steem-js/lib/auth/ecc'; - +import { + PrivateKey, + PublicKey, + Aes, + key_utils, +} from '@steemit/steem-js/lib/auth/ecc'; // import secureRandom from 'secure-random' // import links from 'app/utils/Links' // import assert from 'assert' module.exports = { - - PrivateKey, PublicKey, Aes, key_utils, + PrivateKey, + PublicKey, + Aes, + key_utils, // Run once to start, then again to stop and print a report // https://facebook.github.io/react/docs/perf.html perf: () => { - const Perf = require('react-addons-perf') + const Perf = require('react-addons-perf'); if (perfStarted) { - Perf.stop() - const lm = Perf.getLastMeasurements() - Perf.printInclusive(lm) - Perf.printExclusive(lm) - Perf.printWasted(lm) - perfStarted = false + Perf.stop(); + const lm = Perf.getLastMeasurements(); + Perf.printInclusive(lm); + Perf.printExclusive(lm); + Perf.printWasted(lm); + perfStarted = false; } else { - Perf.start() - perfStarted = true + Perf.start(); + perfStarted = true; } - return Perf + return Perf; }, resolve: (object, atty = '_') => { - if (! object.then) { - console.log(object) - return object + if (!object.then) { + console.log(object); + return object; } return new Promise((resolve, reject) => { - object.then(result => { - console.log(result) - resolve(result) - window[atty] = result - }).catch(error => { - console.error(error) - reject(error) - window[atty] = error - }) - }) + object + .then(result => { + console.log(result); + resolve(result); + window[atty] = result; + }) + .catch(error => { + console.error(error); + reject(error); + window[atty] = error; + }); + }); }, init: context => { - if (! context) return + if (!context) return; for (const obj in module.exports) { - if (obj === 'init') continue - context[obj] = module.exports[obj] + if (obj === 'init') continue; + context[obj] = module.exports[obj]; } }, @@ -62,6 +70,6 @@ module.exports = { // assert(match[0] === 'https://example.com', 'no match') // } // }, -} +}; -let perfStarted = false +let perfStarted = false; diff --git a/src/app/utils/ContentPreview.js b/src/app/utils/ContentPreview.js index bea503b85..6ccbec673 100644 --- a/src/app/utils/ContentPreview.js +++ b/src/app/utils/ContentPreview.js @@ -10,7 +10,7 @@ export default function contentPreview(content, length) { words++; if (words > max_words) break; if (i > length) break; - }; + } res += ch; } return res; diff --git a/src/app/utils/DMCAList.js b/src/app/utils/DMCAList.js index 8e270d9bd..9cceabc63 100644 --- a/src/app/utils/DMCAList.js +++ b/src/app/utils/DMCAList.js @@ -44,4 +44,6 @@ export default ` /photography/@elvinanurhaliza/photohraphy-dewy-flowers /photography/@elvinanurhaliza/spider-finepix-s4800-reptiles-etc /entertainment/@seanfrederic/feature-film-loco-i-m-a-producer-on-starts-filming-soon -`.trim().split('\n'); +` + .trim() + .split('\n'); diff --git a/src/app/utils/DMCAUserList.js b/src/app/utils/DMCAUserList.js index 9bbe44616..85bc4e62d 100644 --- a/src/app/utils/DMCAUserList.js +++ b/src/app/utils/DMCAUserList.js @@ -3,6 +3,8 @@ spaces the-gaming-llama cmgsteems iamgod -`.trim().split('\n'); +` + .trim() + .split('\n'); export default list; diff --git a/src/app/utils/DomUtils.js b/src/app/utils/DomUtils.js index 36021a826..4a5e99f49 100644 --- a/src/app/utils/DomUtils.js +++ b/src/app/utils/DomUtils.js @@ -1,5 +1,10 @@ export function findParent(el, class_name) { - if (el.className && el.className.indexOf && el.className.indexOf(class_name) !== -1) return el; + if ( + el.className && + el.className.indexOf && + el.className.indexOf(class_name) !== -1 + ) + return el; if (el.parentNode) return findParent(el.parentNode, class_name); return null; } diff --git a/src/app/utils/ExtractContent.js b/src/app/utils/ExtractContent.js index f972c0895..c8e07e19f 100644 --- a/src/app/utils/ExtractContent.js +++ b/src/app/utils/ExtractContent.js @@ -1,11 +1,11 @@ -import remarkableStripper from 'app/utils/RemarkableStripper' -import links from 'app/utils/Links' -import sanitize from 'sanitize-html' -import {htmlDecode} from 'app/utils/Html' -import HtmlReady from 'shared/HtmlReady' -import Remarkable from 'remarkable' +import remarkableStripper from 'app/utils/RemarkableStripper'; +import links from 'app/utils/Links'; +import sanitize from 'sanitize-html'; +import { htmlDecode } from 'app/utils/Html'; +import HtmlReady from 'shared/HtmlReady'; +import Remarkable from 'remarkable'; -const remarkable = new Remarkable({ html: true, linkify: false }) +const remarkable = new Remarkable({ html: true, linkify: false }); export default function extractContent(get, content) { const { @@ -18,7 +18,7 @@ export default function extractContent(get, content) { title, created, net_rshares, - children + children, } = get( content, 'author', @@ -36,69 +36,83 @@ export default function extractContent(get, content) { let link = `/@${author}/${permlink}`; if (category) link = `/${category}${link}`; const body = get(content, 'body'); - let jsonMetadata = {} - let image_link + let jsonMetadata = {}; + let image_link; try { - jsonMetadata = JSON.parse(json_metadata) - if(typeof jsonMetadata == 'string') { + jsonMetadata = JSON.parse(json_metadata); + if (typeof jsonMetadata == 'string') { // At least one case where jsonMetadata was double-encoded: #895 - jsonMetadata = JSON.parse(jsonMetadata) + jsonMetadata = JSON.parse(jsonMetadata); } // First, attempt to find an image url in the json metadata - if(jsonMetadata) { - if(jsonMetadata.image && Array.isArray(jsonMetadata.image)) { - [image_link] = jsonMetadata.image + if (jsonMetadata) { + if (jsonMetadata.image && Array.isArray(jsonMetadata.image)) { + [image_link] = jsonMetadata.image; } } - } catch(error) { + } catch (error) { // console.error('Invalid json metadata string', json_metadata, 'in post', author, permlink); } // If nothing found in json metadata, parse body and check images/links - if(!image_link) { - let rtags + if (!image_link) { + let rtags; { - const isHtml = /^([\S\s]*)<\/html>$/.test(body) - const htmlText = isHtml ? body : remarkable.render(body.replace(/|$)/g, '(html comment removed: $1)')) - rtags = HtmlReady(htmlText, {mutate: false}) + const isHtml = /^([\S\s]*)<\/html>$/.test(body); + const htmlText = isHtml + ? body + : remarkable.render( + body.replace( + /|$)/g, + '(html comment removed: $1)' + ) + ); + rtags = HtmlReady(htmlText, { mutate: false }); } - [image_link] = Array.from(rtags.images) + [image_link] = Array.from(rtags.images); } // Was causing broken thumnails. IPFS was not finding images uploaded to another server until a restart. // if(config.ipfs_prefix && image_link) // allow localhost nodes to see ipfs images // image_link = image_link.replace(links.ipfsPrefix, config.ipfs_prefix) - let desc - let desc_complete = false - if(!desc) { + let desc; + let desc_complete = false; + if (!desc) { // Short description. // Remove bold and header, etc. // Stripping removes links with titles (so we got the links above).. // Remove block quotes if detected at beginning of comment preview if comment has a parent - const body2 = remarkableStripper.render(get(content, 'depth') > 1 ? body.replace(/(^(\n|\r|\s)*)>([\s\S]*?).*\s*/g, '') : body); - desc = sanitize(body2, {allowedTags: []})// remove all html, leaving text - desc = htmlDecode(desc) + const body2 = remarkableStripper.render( + get(content, 'depth') > 1 + ? body.replace(/(^(\n|\r|\s)*)>([\s\S]*?).*\s*/g, '') + : body + ); + desc = sanitize(body2, { allowedTags: [] }); // remove all html, leaving text + desc = htmlDecode(desc); // Strip any raw URLs from preview text desc = desc.replace(/https?:\/\/[^\s]+/g, ''); // Grab only the first line (not working as expected. does rendering/sanitizing strip newlines?) - desc = desc.trim().split("\n")[0]; + desc = desc.trim().split('\n')[0]; - if(desc.length > 140) { - desc = desc.substring(0, 140).trim(); + if (desc.length > 140) { + desc = desc.substring(0, 140).trim(); - const dotSpace = desc.lastIndexOf('. ') - if(dotSpace > 80 && !get(content, 'depth') > 1) { - desc = desc.substring(0, dotSpace + 1) - } else { - // Truncate, remove the last (likely partial) word (along with random punctuation), and add ellipses - desc = desc.substring(0, 120).trim().replace(/[,!\?]?\s+[^\s]+$/, "…"); - } + const dotSpace = desc.lastIndexOf('. '); + if (dotSpace > 80 && !get(content, 'depth') > 1) { + desc = desc.substring(0, dotSpace + 1); + } else { + // Truncate, remove the last (likely partial) word (along with random punctuation), and add ellipses + desc = desc + .substring(0, 120) + .trim() + .replace(/[,!\?]?\s+[^\s]+$/, '…'); + } } - desc_complete = body2 === desc // is the entire body in desc? + desc_complete = body2 === desc; // is the entire body in desc? } const pending_payout = get(content, 'pending_payout_value'); return { diff --git a/src/app/utils/ExtractMeta.js b/src/app/utils/ExtractMeta.js index e2745db51..97c113d7e 100644 --- a/src/app/utils/ExtractMeta.js +++ b/src/app/utils/ExtractMeta.js @@ -1,86 +1,115 @@ import extractContent from 'app/utils/ExtractContent'; -import {objAccessor} from 'app/utils/Accessors'; +import { objAccessor } from 'app/utils/Accessors'; import normalizeProfile from 'app/utils/NormalizeProfile'; -const site_desc = 'Steemit is a social media platform where everyone gets paid for creating and curating content. It leverages a robust digital points system (Steem) for digital rewards.'; +const site_desc = + 'Steemit is a social media platform where everyone gets paid for creating and curating content. It leverages a robust digital points system (Steem) for digital rewards.'; function addSiteMeta(metas) { - metas.push({title: 'Steemit'}); - metas.push({name: 'description', content: site_desc}); - metas.push({property: 'og:type', content: 'website'}); - metas.push({property: 'og:site_name', content: 'Steemit'}); - metas.push({property: 'og:title', content: 'Steemit'}); - metas.push({property: 'og:description', content: site_desc}); - metas.push({property: 'og:image', content: 'https://steemit.com/images/steemit.png'}); - metas.push({property: 'fb:app_id', content: $STM_Config.fb_app}); - metas.push({name: 'twitter:card', content: 'summary'}); - metas.push({name: 'twitter:site', content: '@steemit'}); - metas.push({name: 'twitter:title', content: '#Steemit'}); - metas.push({name: 'twitter:description', site_desc}); - metas.push({name: 'twitter:image', content: 'https://steemit.com/images/steemit.png'}); + metas.push({ title: 'Steemit' }); + metas.push({ name: 'description', content: site_desc }); + metas.push({ property: 'og:type', content: 'website' }); + metas.push({ property: 'og:site_name', content: 'Steemit' }); + metas.push({ property: 'og:title', content: 'Steemit' }); + metas.push({ property: 'og:description', content: site_desc }); + metas.push({ + property: 'og:image', + content: 'https://steemit.com/images/steemit.png', + }); + metas.push({ property: 'fb:app_id', content: $STM_Config.fb_app }); + metas.push({ name: 'twitter:card', content: 'summary' }); + metas.push({ name: 'twitter:site', content: '@steemit' }); + metas.push({ name: 'twitter:title', content: '#Steemit' }); + metas.push({ name: 'twitter:description', site_desc }); + metas.push({ + name: 'twitter:image', + content: 'https://steemit.com/images/steemit.png', + }); } export default function extractMeta(chain_data, rp) { const metas = []; - if (rp.username && rp.slug) { // post + if (rp.username && rp.slug) { + // post const post = `${rp.username}/${rp.slug}`; const content = chain_data.content[post]; - const author = chain_data.accounts[rp.username]; + const author = chain_data.accounts[rp.username]; const profile = normalizeProfile(author); - if (content && content.id !== '0.0.0') { // API currently returns 'false' data with id 0.0.0 for posts that do not exist + if (content && content.id !== '0.0.0') { + // API currently returns 'false' data with id 0.0.0 for posts that do not exist const d = extractContent(objAccessor, content, false); - const url = 'https://steemit.com' + d.link; + const url = 'https://steemit.com' + d.link; const title = d.title + ' — Steemit'; - const desc = d.desc + " by " + d.author; - const image = d.image_link || profile.profile_image - const {category, created} = d + const desc = d.desc + ' by ' + d.author; + const image = d.image_link || profile.profile_image; + const { category, created } = d; // Standard meta - metas.push({title}); - metas.push({canonical: url}); - metas.push({name: 'description', content: desc}); + metas.push({ title }); + metas.push({ canonical: url }); + metas.push({ name: 'description', content: desc }); // Open Graph data - metas.push({property: 'og:title', content: title}); - metas.push({property: 'og:type', content: 'article'}); - metas.push({property: 'og:url', content: url}); - metas.push({property: 'og:image', content: image || 'https://steemit.com/images/steemit.png'}); - metas.push({property: 'og:description', content: desc}); - metas.push({property: 'og:site_name', content: 'Steemit'}); - metas.push({property: 'fb:app_id', content: $STM_Config.fb_app}); - metas.push({property: 'article:tag', content: category}); - metas.push({property: 'article:published_time', content: created}); + metas.push({ property: 'og:title', content: title }); + metas.push({ property: 'og:type', content: 'article' }); + metas.push({ property: 'og:url', content: url }); + metas.push({ + property: 'og:image', + content: image || 'https://steemit.com/images/steemit.png', + }); + metas.push({ property: 'og:description', content: desc }); + metas.push({ property: 'og:site_name', content: 'Steemit' }); + metas.push({ property: 'fb:app_id', content: $STM_Config.fb_app }); + metas.push({ property: 'article:tag', content: category }); + metas.push({ + property: 'article:published_time', + content: created, + }); // Twitter card data - metas.push({name: 'twitter:card', content: image ? 'summary_large_image' : 'summary'}); - metas.push({name: 'twitter:site', content: '@steemit'}); - metas.push({name: 'twitter:title', content: title}); - metas.push({name: 'twitter:description', content: desc}); - metas.push({name: 'twitter:image', content: image || 'https://steemit.com/images/steemit-twshare-2.png'}); + metas.push({ + name: 'twitter:card', + content: image ? 'summary_large_image' : 'summary', + }); + metas.push({ name: 'twitter:site', content: '@steemit' }); + metas.push({ name: 'twitter:title', content: title }); + metas.push({ name: 'twitter:description', content: desc }); + metas.push({ + name: 'twitter:image', + content: + image || 'https://steemit.com/images/steemit-twshare-2.png', + }); } else { addSiteMeta(metas); } - } else if (rp.accountname) { // user profile root + } else if (rp.accountname) { + // user profile root const account = chain_data.accounts[rp.accountname]; - let {name, about, profile_image} = normalizeProfile(account); - if(name == null) name = account.name; - if(about == null) about = "Join thousands on steemit who share, post and earn rewards."; - if(profile_image == null) profile_image = 'https://steemit.com/images/steemit-twshare-2.png'; + let { name, about, profile_image } = normalizeProfile(account); + if (name == null) name = account.name; + if (about == null) + about = + 'Join thousands on steemit who share, post and earn rewards.'; + if (profile_image == null) + profile_image = 'https://steemit.com/images/steemit-twshare-2.png'; // Set profile tags const title = `@${account.name}`; - const desc = `The latest posts from ${name}. Follow me at @${account.name}. ${about}`; + const desc = `The latest posts from ${name}. Follow me at @${ + account.name + }. ${about}`; const image = profile_image; // Standard meta - metas.push({name: 'description', content: desc}); + metas.push({ name: 'description', content: desc }); // Twitter card data - metas.push({name: 'twitter:card', content: 'summary'}); - metas.push({name: 'twitter:site', content: '@steemit'}); - metas.push({name: 'twitter:title', content: title}); - metas.push({name: 'twitter:description', content: desc}); - metas.push({name: 'twitter:image', content: image}); - } else { // site + metas.push({ name: 'twitter:card', content: 'summary' }); + metas.push({ name: 'twitter:site', content: '@steemit' }); + metas.push({ name: 'twitter:title', content: title }); + metas.push({ name: 'twitter:description', content: desc }); + metas.push({ name: 'twitter:image', content: image }); + } else { + // site addSiteMeta(metas); } return metas; diff --git a/src/app/utils/FormatCoins.js b/src/app/utils/FormatCoins.js index 390ed2948..727447f9e 100644 --- a/src/app/utils/FormatCoins.js +++ b/src/app/utils/FormatCoins.js @@ -1,15 +1,27 @@ -import { APP_NAME, LIQUID_TOKEN, LIQUID_TOKEN_UPPERCASE, DEBT_TOKEN, DEBT_TOKEN_SHORT, CURRENCY_SIGN, VESTING_TOKEN } from 'app/client_config'; +import { + APP_NAME, + LIQUID_TOKEN, + LIQUID_TOKEN_UPPERCASE, + DEBT_TOKEN, + DEBT_TOKEN_SHORT, + CURRENCY_SIGN, + VESTING_TOKEN, +} from 'app/client_config'; // TODO add comments and explanations // TODO change name to formatCoinTypes? // TODO make use of DEBT_TICKER etc defined in config/clietn_config export function formatCoins(string) { - // return null or undefined if string is not provided - if(!string) return string - // TODO use .to:owerCase() ? for string normalisation - string = string.replace('SBD', DEBT_TOKEN_SHORT ).replace('SD', DEBT_TOKEN_SHORT) - .replace('Steem Power', VESTING_TOKEN).replace('STEEM POWER', VESTING_TOKEN) - .replace('Steem', LIQUID_TOKEN).replace('STEEM', LIQUID_TOKEN_UPPERCASE) - .replace('$', CURRENCY_SIGN) - return string + // return null or undefined if string is not provided + if (!string) return string; + // TODO use .to:owerCase() ? for string normalisation + string = string + .replace('SBD', DEBT_TOKEN_SHORT) + .replace('SD', DEBT_TOKEN_SHORT) + .replace('Steem Power', VESTING_TOKEN) + .replace('STEEM POWER', VESTING_TOKEN) + .replace('Steem', LIQUID_TOKEN) + .replace('STEEM', LIQUID_TOKEN_UPPERCASE) + .replace('$', CURRENCY_SIGN); + return string; } diff --git a/src/app/utils/FormatDecimal.test.js b/src/app/utils/FormatDecimal.test.js index 044ff0138..ae6dc05b3 100644 --- a/src/app/utils/FormatDecimal.test.js +++ b/src/app/utils/FormatDecimal.test.js @@ -1,8 +1,8 @@ /*global describe, it, before, beforeEach, after, afterEach */ -import chai, {expect} from 'chai'; +import chai, { expect } from 'chai'; import dirtyChai from 'dirty-chai'; -import {formatDecimal} from './ParsersAndFormatters'; +import { formatDecimal } from './ParsersAndFormatters'; chai.use(dirtyChai); describe('formatDecimal', () => { @@ -13,7 +13,7 @@ describe('formatDecimal', () => { ['102', '102.00'], [1000.12, '1,000.12'], [100000, '100,000.00'], - [1000000000000.00, '1,000,000,000,000.00'], + [1000000000000.0, '1,000,000,000,000.00'], [-1000, '-1,000.00'], ]; test_cases.forEach(v => { diff --git a/src/app/utils/Html.js b/src/app/utils/Html.js index 7ebb8abae..4ecc1fd92 100644 --- a/src/app/utils/Html.js +++ b/src/app/utils/Html.js @@ -1,8 +1,8 @@ - -export const htmlDecode = txt => txt.replace(/&[a-z]+;/g, ch => { - const char = htmlCharMap[ch.substring(1, ch.length - 1)] - return char ? char : ch -}) +export const htmlDecode = txt => + txt.replace(/&[a-z]+;/g, ch => { + const char = htmlCharMap[ch.substring(1, ch.length - 1)]; + return char ? char : ch; + }); const htmlCharMap = { amp: '&', @@ -17,5 +17,5 @@ const htmlCharMap = { trade: '™', hellip: '…', pound: '£', - copy: '' -} + copy: '', +}; diff --git a/src/app/utils/ImageUserBlockList.js b/src/app/utils/ImageUserBlockList.js index a439d57b4..12a8c911e 100644 --- a/src/app/utils/ImageUserBlockList.js +++ b/src/app/utils/ImageUserBlockList.js @@ -1,5 +1,7 @@ const list = ` iamgod -`.trim().split('\n'); +` + .trim() + .split('\n'); export default list; diff --git a/src/app/utils/JsPlugins.js b/src/app/utils/JsPlugins.js index 3009b2a35..0cf06f8cb 100644 --- a/src/app/utils/JsPlugins.js +++ b/src/app/utils/JsPlugins.js @@ -1,38 +1,47 @@ // 3rd party plugins export default function init(config) { - if (config.google_analytics_id) { - (function (i, s, o, g, r, a, m) { + (function(i, s, o, g, r, a, m) { i['GoogleAnalyticsObject'] = r; - i[r] = i[r] || function () { - (i[r].q = i[r].q || []).push(arguments) - }, i[r].l = 1 * new Date(); - a = s.createElement(o), - m = s.getElementsByTagName(o)[0]; + (i[r] = + i[r] || + function() { + (i[r].q = i[r].q || []).push(arguments); + }), + (i[r].l = 1 * new Date()); + (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]); a.async = 1; a.src = g; - m.parentNode.insertBefore(a, m) - })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); + m.parentNode.insertBefore(a, m); + })( + window, + document, + 'script', + 'https://www.google-analytics.com/analytics.js', + 'ga' + ); ga('create', config.google_analytics_id, 'auto'); } if (config.facebook_app_id) { - window.fbAsyncInit = function () { + window.fbAsyncInit = function() { FB.init({ appId: config.facebook_app_id, xfbml: true, - version: 'v2.6' + version: 'v2.6', }); }; - (function (d, s, id) { - var js, fjs = d.getElementsByTagName(s)[0]; - if (d.getElementById(id)) {return;} + (function(d, s, id) { + var js, + fjs = d.getElementsByTagName(s)[0]; + if (d.getElementById(id)) { + return; + } js = d.createElement(s); js.id = id; - js.src = "//connect.facebook.net/en_US/sdk.js"; + js.src = '//connect.facebook.net/en_US/sdk.js'; fjs.parentNode.insertBefore(js, fjs); - }(document, 'script', 'facebook-jssdk')); + })(document, 'script', 'facebook-jssdk'); } - } diff --git a/src/app/utils/Links.js b/src/app/utils/Links.js index 714fd75ca..ce1137a2f 100644 --- a/src/app/utils/Links.js +++ b/src/app/utils/Links.js @@ -1,25 +1,38 @@ -import {PARAM_VIEW_MODE, VIEW_MODE_WHISTLE} from "../../shared/constants"; +import { PARAM_VIEW_MODE, VIEW_MODE_WHISTLE } from '../../shared/constants'; -const urlChar = '[^\\s"<>\\]\\[\\(\\)]' -const urlCharEnd = urlChar.replace(/\]$/, '.,\']') // insert bad chars to end on -const imagePath = '(?:(?:\\.(?:tiff?|jpe?g|gif|png|svg|ico)|ipfs/[a-z\\d]{40,}))' -const domainPath = '(?:[-a-zA-Z0-9\\._]*[-a-zA-Z0-9])' -const urlChars = '(?:' + urlChar + '*' + urlCharEnd + ')?' +const urlChar = '[^\\s"<>\\]\\[\\(\\)]'; +const urlCharEnd = urlChar.replace(/\]$/, ".,']"); // insert bad chars to end on +const imagePath = + '(?:(?:\\.(?:tiff?|jpe?g|gif|png|svg|ico)|ipfs/[a-z\\d]{40,}))'; +const domainPath = '(?:[-a-zA-Z0-9\\._]*[-a-zA-Z0-9])'; +const urlChars = '(?:' + urlChar + '*' + urlCharEnd + ')?'; -const urlSet = ({domain = domainPath, path} = {}) => { - // urlChars is everything but html or markdown stop chars - return `https?:\/\/${domain}(?::\\d{2,5})?(?:[/\\?#]${urlChars}${path ? path : ''})${path ? '' : '?'}` -} +const urlSet = ({ domain = domainPath, path } = {}) => { + // urlChars is everything but html or markdown stop chars + return `https?:\/\/${domain}(?::\\d{2,5})?(?:[/\\?#]${urlChars}${ + path ? path : '' + })${path ? '' : '?'}`; +}; /** Unless your using a 'g' (glob) flag you can store and re-use your regular expression. Use the cache below. If your using a glob (for example: replace all), the regex object becomes stateful and continues where it left off when called with the same string so naturally the regexp object can't be cached for long. */ -export const any = (flags = 'i') => new RegExp(urlSet(), flags) -export const local = (flags = 'i') => new RegExp(urlSet({domain: '(?:localhost|(?:.*\\.)?steemit.com)'}), flags) -export const remote = (flags = 'i') => new RegExp(urlSet({domain: `(?!localhost|(?:.*\\.)?steemit.com)${domainPath}`}), flags) -export const youTube = (flags = 'i') => new RegExp(urlSet({domain: '(?:(?:.*\.)?youtube.com|youtu.be)'}), flags) -export const image = (flags = 'i') => new RegExp(urlSet({path: imagePath}), flags) -export const imageFile = (flags = 'i') => new RegExp(imagePath, flags) +export const any = (flags = 'i') => new RegExp(urlSet(), flags); +export const local = (flags = 'i') => + new RegExp( + urlSet({ domain: '(?:localhost|(?:.*\\.)?steemit.com)' }), + flags + ); +export const remote = (flags = 'i') => + new RegExp( + urlSet({ domain: `(?!localhost|(?:.*\\.)?steemit.com)${domainPath}` }), + flags + ); +export const youTube = (flags = 'i') => + new RegExp(urlSet({ domain: '(?:(?:.*.)?youtube.com|youtu.be)' }), flags); +export const image = (flags = 'i') => + new RegExp(urlSet({ path: imagePath }), flags); +export const imageFile = (flags = 'i') => new RegExp(imagePath, flags); // export const nonImage = (flags = 'i') => new RegExp(urlSet({path: '!' + imageFile}), flags) // export const markDownImageRegExp = (flags = 'i') => new RegExp('\!\[[\w\s]*\]\(([^\)]+)\)', flags); @@ -34,7 +47,7 @@ export default { vimeoId: /(?:vimeo.com\/|player.vimeo.com\/video\/)([0-9]+)/, // simpleLink: new RegExp(`(.*)<\/a>`, 'ig'), ipfsPrefix: /(https?:\/\/.*)?\/ipfs/i, -} +}; //TODO: possible this should go somewhere else. /** @@ -47,49 +60,56 @@ export default { */ export const addToParams = (outputParams, inputParams, key, allowedValues) => { const respParams = Object.assign({}, outputParams); - if(inputParams[key] && allowedValues.indexOf(inputParams[key]) > -1) { + if (inputParams[key] && allowedValues.indexOf(inputParams[key]) > -1) { respParams[key] = inputParams[key]; } return respParams; -} +}; //TODO: possible this should go somewhere else. export const makeParams = (params, prefix) => { let paramsList = []; - if(params.constructor === Array) { + if (params.constructor === Array) { paramsList = params; } else { Object.entries(params).forEach(([key, value]) => { paramsList.push(`${key}=${value}`); }); } - if(paramsList.length > 0) { - return ((prefix !== false)? ((typeof prefix === 'string')? prefix : '?') : '') + paramsList.join('&'); + if (paramsList.length > 0) { + return ( + (prefix !== false + ? typeof prefix === 'string' ? prefix : '?' + : '') + paramsList.join('&') + ); } return ''; -} +}; /** * * @param {string} search - window.location.search formatted string (may omit '?') * @returns {string} */ -export const determineViewMode = (search) => { - const searchList = (search.indexOf('?') === 0) ? search.substr(1).split('&') : search.split('&'); - for(let i = 0; i < searchList.length; i++) { - if(searchList[i].indexOf(PARAM_VIEW_MODE) === 0) { - if(searchList[i] == (PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE)) { //we only want to support known view modes. +export const determineViewMode = search => { + const searchList = + search.indexOf('?') === 0 + ? search.substr(1).split('&') + : search.split('&'); + for (let i = 0; i < searchList.length; i++) { + if (searchList[i].indexOf(PARAM_VIEW_MODE) === 0) { + if (searchList[i] == PARAM_VIEW_MODE + '=' + VIEW_MODE_WHISTLE) { + //we only want to support known view modes. return VIEW_MODE_WHISTLE; } return ''; } } return ''; -} +}; // Original regex // const urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$'; // About performance // Using exec on the same regex object requires a new regex to be created and compile for each text (ex: post). Instead replace can be used `body.replace(remoteRe, l => {` discarding the result for better performance`}). Re-compiling is a chrome bottleneck but did not effect nodejs. - diff --git a/src/app/utils/Links.test.js b/src/app/utils/Links.test.js index 48c3bf88c..b5aff96ad 100644 --- a/src/app/utils/Links.test.js +++ b/src/app/utils/Links.test.js @@ -1,152 +1,310 @@ - -import assert from 'assert' -import secureRandom from 'secure-random' -import links, * as linksRe from 'app/utils/Links' -import {PARAM_VIEW_MODE, VIEW_MODE_WHISTLE} from "../../shared/constants"; +import assert from 'assert'; +import secureRandom from 'secure-random'; +import links, * as linksRe from 'app/utils/Links'; +import { PARAM_VIEW_MODE, VIEW_MODE_WHISTLE } from '../../shared/constants'; describe('Links', () => { it('all', () => { - match(linksRe.any(), 'https://example.com/wiki/Poe\'s_law', 'https://example.com/wiki/Poe\'s_law') - match(linksRe.any(), 'https://example.com\'', 'https://example.com') - match(linksRe.any(), '"https://example.com', 'https://example.com') - match(linksRe.any(), 'https://example.com\"', 'https://example.com') - match(linksRe.any(), 'https://example.com\'', 'https://example.com') - match(linksRe.any(), 'https://example.com<', 'https://example.com') - match(linksRe.any(), 'https://example.com>', 'https://example.com') - match(linksRe.any(), 'https://example.com\n', 'https://example.com') - match(linksRe.any(), ' https://example.com ', 'https://example.com') - match(linksRe.any(), 'https://example.com ', 'https://example.com') - match(linksRe.any(), 'https://example.com.', 'https://example.com') - match(linksRe.any(), 'https://example.com/page.', 'https://example.com/page') - match(linksRe.any(), 'https://example.com,', 'https://example.com') - match(linksRe.any(), 'https://example.com/page,', 'https://example.com/page') - }) + match( + linksRe.any(), + "https://example.com/wiki/Poe's_law", + "https://example.com/wiki/Poe's_law" + ); + match(linksRe.any(), "https://example.com'", 'https://example.com'); + match(linksRe.any(), '"https://example.com', 'https://example.com'); + match(linksRe.any(), 'https://example.com"', 'https://example.com'); + match(linksRe.any(), "https://example.com'", 'https://example.com'); + match(linksRe.any(), 'https://example.com<', 'https://example.com'); + match(linksRe.any(), 'https://example.com>', 'https://example.com'); + match(linksRe.any(), 'https://example.com\n', 'https://example.com'); + match(linksRe.any(), ' https://example.com ', 'https://example.com'); + match(linksRe.any(), 'https://example.com ', 'https://example.com'); + match(linksRe.any(), 'https://example.com.', 'https://example.com'); + match( + linksRe.any(), + 'https://example.com/page.', + 'https://example.com/page' + ); + match(linksRe.any(), 'https://example.com,', 'https://example.com'); + match( + linksRe.any(), + 'https://example.com/page,', + 'https://example.com/page' + ); + }); it('multiple matches', () => { - const all = linksRe.any('ig') - let match = all.exec('\nhttps://example.com/1\nhttps://example.com/2') - assert.equal(match[0], 'https://example.com/1') - match = all.exec('https://example.com/1 https://example.com/2') - assert.equal(match[0], 'https://example.com/2') - }) + const all = linksRe.any('ig'); + let match = all.exec('\nhttps://example.com/1\nhttps://example.com/2'); + assert.equal(match[0], 'https://example.com/1'); + match = all.exec('https://example.com/1 https://example.com/2'); + assert.equal(match[0], 'https://example.com/2'); + }); it('by domain', () => { - const locals = ['https://localhost/', 'http://steemit.com', 'http://steemit.com/group'] - match(linksRe.local(), locals) - matchNot(linksRe.remote(), locals) + const locals = [ + 'https://localhost/', + 'http://steemit.com', + 'http://steemit.com/group', + ]; + match(linksRe.local(), locals); + matchNot(linksRe.remote(), locals); - const remotes = ['https://example.com/', 'http://abc.co'] - match(linksRe.remote(), remotes) - matchNot(linksRe.local(), remotes) + const remotes = ['https://example.com/', 'http://abc.co']; + match(linksRe.remote(), remotes); + matchNot(linksRe.local(), remotes); // match(linksRe({external: false}), largeData + 'https://steemit.com2/next', 'https://steemit.com2/next') - }) + }); it('by image', () => { - match(linksRe.image(), 'https://example.com/a.jpeg') - match(linksRe.image(), 'https://example.com/a/b.jpeg') - match(linksRe.image(), '![](https://example.com/img2/nehoshtanit.jpg)', 'https://example.com/img2/nehoshtanit.jpg') - match(linksRe.image(), ' { it('creates an empty string when there are no params', () => { assert(linksRe.makeParams([]) === '', 'not empty on array'); assert(linksRe.makeParams({}) === '', 'not empty on object'); - assert(linksRe.makeParams({}, false) === '', 'not empty on object with prefix false'); - assert(linksRe.makeParams([], false) === '', 'not empty on array with prefix false'); - assert(linksRe.makeParams([], '?') === '', 'not empty on array with prefix string'); - assert(linksRe.makeParams({}, '?') === '', 'not empty on object with prefix string'); + assert( + linksRe.makeParams({}, false) === '', + 'not empty on object with prefix false' + ); + assert( + linksRe.makeParams([], false) === '', + 'not empty on array with prefix false' + ); + assert( + linksRe.makeParams([], '?') === '', + 'not empty on array with prefix string' + ); + assert( + linksRe.makeParams({}, '?') === '', + 'not empty on object with prefix string' + ); }); it('creates the correct string when passed an array', () => { - assert(linksRe.makeParams(['bop=boop','troll=bridge']) === '?bop=boop&troll=bridge', 'incorrect string with'); - assert(linksRe.makeParams(['bop=boop','troll=bridge'], false) === 'bop=boop&troll=bridge', 'incorrect string with prefix false'); - assert(linksRe.makeParams(['bop=boop','troll=bridge'], '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &'); + assert( + linksRe.makeParams(['bop=boop', 'troll=bridge']) === + '?bop=boop&troll=bridge', + 'incorrect string with' + ); + assert( + linksRe.makeParams(['bop=boop', 'troll=bridge'], false) === + 'bop=boop&troll=bridge', + 'incorrect string with prefix false' + ); + assert( + linksRe.makeParams(['bop=boop', 'troll=bridge'], '&') === + '&bop=boop&troll=bridge', + 'incorrect string with prefix &' + ); }); it('creates the correct string when passed an object', () => { - assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}) === '?bop=boop&troll=bridge', 'incorrect string'); - assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, false) === 'bop=boop&troll=bridge', 'incorrect string with prefix false'); - assert(linksRe.makeParams({bop: 'boop',troll: 'bridge'}, '&') === '&bop=boop&troll=bridge', 'incorrect string with prefix &'); + assert( + linksRe.makeParams({ bop: 'boop', troll: 'bridge' }) === + '?bop=boop&troll=bridge', + 'incorrect string' + ); + assert( + linksRe.makeParams({ bop: 'boop', troll: 'bridge' }, false) === + 'bop=boop&troll=bridge', + 'incorrect string with prefix false' + ); + assert( + linksRe.makeParams({ bop: 'boop', troll: 'bridge' }, '&') === + '&bop=boop&troll=bridge', + 'incorrect string with prefix &' + ); }); }); describe('determineViewMode', () => { it('returns empty string when no parameter in search', () => { - assert(linksRe.determineViewMode('') === '', linksRe.determineViewMode('') + 'not empty on empty string'); - assert(linksRe.determineViewMode('?afs=asdf') === '', 'not empty on incorrect parameter'); - assert(linksRe.determineViewMode('?afs=asdf&apple=sauce') === '', 'not empty on incorrect parameter'); + assert( + linksRe.determineViewMode('') === '', + linksRe.determineViewMode('') + 'not empty on empty string' + ); + assert( + linksRe.determineViewMode('?afs=asdf') === '', + 'not empty on incorrect parameter' + ); + assert( + linksRe.determineViewMode('?afs=asdf&apple=sauce') === '', + 'not empty on incorrect parameter' + ); }); it('returns empty string when unrecognized value for parameter in search', () => { - assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=asd`) === '', 'not empty on incorrect parameter value'); - assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}1`) === '', 'not empty on incorrect parameter value'); - assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=asdf&apple=sauce`) === '', 'not empty on incorrect parameter value'); - assert(linksRe.determineViewMode(`?apple=sauce&${PARAM_VIEW_MODE}=asdf`) === '', 'not empty on incorrect parameter value'); + assert( + linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=asd`) === '', + 'not empty on incorrect parameter value' + ); + assert( + linksRe.determineViewMode( + `?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}1` + ) === '', + 'not empty on incorrect parameter value' + ); + assert( + linksRe.determineViewMode( + `?${PARAM_VIEW_MODE}=asdf&apple=sauce` + ) === '', + 'not empty on incorrect parameter value' + ); + assert( + linksRe.determineViewMode( + `?apple=sauce&${PARAM_VIEW_MODE}=asdf` + ) === '', + 'not empty on incorrect parameter value' + ); }); it('returns correct value when recognized value for parameter in search', () => { - assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}`) === VIEW_MODE_WHISTLE, 'wrong response on correct parameter'); - assert(linksRe.determineViewMode(`?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}&apple=sauce`) === VIEW_MODE_WHISTLE, 'wrong response on correct parameter'); - assert(linksRe.determineViewMode(`?apple=sauce&${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}`) === VIEW_MODE_WHISTLE, 'wrong response on correct parameter'); + assert( + linksRe.determineViewMode( + `?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}` + ) === VIEW_MODE_WHISTLE, + 'wrong response on correct parameter' + ); + assert( + linksRe.determineViewMode( + `?${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}&apple=sauce` + ) === VIEW_MODE_WHISTLE, + 'wrong response on correct parameter' + ); + assert( + linksRe.determineViewMode( + `?apple=sauce&${PARAM_VIEW_MODE}=${VIEW_MODE_WHISTLE}` + ) === VIEW_MODE_WHISTLE, + 'wrong response on correct parameter' + ); }); }); // 1st in the browser it is very expensive to re-create a regular expression many times, however, in nodejs is is very in-expensive (it is as if it is caching it). describe('Performance', () => { - const largeData = secureRandom.randomBuffer(1024 * 10).toString('hex') + const largeData = secureRandom.randomBuffer(1024 * 10).toString('hex'); it('any, ' + largeData.length + ' bytes x 10,000', () => { for (let i = 0; i < 10000; i++) { - const match = (largeData + 'https://example.com').match(linksRe.any()) - assert(match, 'no match') - assert(match[0] === 'https://example.com', 'no match') + const match = (largeData + 'https://example.com').match( + linksRe.any() + ); + assert(match, 'no match'); + assert(match[0] === 'https://example.com', 'no match'); } - }) + }); it('image (large), ' + largeData.length + ' bytes x 10,000', () => { for (let i = 0; i < 10000; i++) { - matchNot(linksRe.image(), 'https://lh3.googleusercontent.com/OehcduRZPcVIX_2tlOKgYHADtBvorTfL4JtjfGAPWZyiiI9p_g2ZKEUKfuv3By-aiVfirXaYvEsViJEbxts6IeVYqidnpgkkkXAe0Q79_ARXX6CU5hBK2sZaHKa20U3jBzYbMxT-OVNX8-JYf-GYa2geUQa6pVpUDY35iaiiNBObF-TMIUOqm0P61gCdukTFwLgld2BBlxoVNNt_w6VglYHJP0W4izVNkEu7ugrU-qf2Iw9hb22SGIFNpbzL_ldomDMthIuYfKSYGsqe2ClvNKRz-_vVCQr7ggRXra16uQOdUUv5IVnkK67p9yR8ioajJ4tiGdzazYVow46pbeZ76i9_NoEYnOEX2_a7niofnC5BgAjoQEeoes1cMWVM7V8ZSexBA-cxmi0EVLds4RBkInvaUZjVL7h3oJ5I19GugPTzlyVyYtkf1ej6LNttkagqHgMck87UQGvCbwDX9ECTngffwQPYZlZKnthW0DlkFGgHN8T9uqEpl-3ki50gTa6gC0Q16mEeDRKZe7_g5Sw52OjMsfWxmBBWWMSHzlQKKAIKMKKaD6Td0O_zpiXXp7Fyl7z_iESvCpOAUAIKnyJyF_Y0UYktEmw=w2066-h1377-no') + matchNot( + linksRe.image(), + 'https://lh3.googleusercontent.com/OehcduRZPcVIX_2tlOKgYHADtBvorTfL4JtjfGAPWZyiiI9p_g2ZKEUKfuv3By-aiVfirXaYvEsViJEbxts6IeVYqidnpgkkkXAe0Q79_ARXX6CU5hBK2sZaHKa20U3jBzYbMxT-OVNX8-JYf-GYa2geUQa6pVpUDY35iaiiNBObF-TMIUOqm0P61gCdukTFwLgld2BBlxoVNNt_w6VglYHJP0W4izVNkEu7ugrU-qf2Iw9hb22SGIFNpbzL_ldomDMthIuYfKSYGsqe2ClvNKRz-_vVCQr7ggRXra16uQOdUUv5IVnkK67p9yR8ioajJ4tiGdzazYVow46pbeZ76i9_NoEYnOEX2_a7niofnC5BgAjoQEeoes1cMWVM7V8ZSexBA-cxmi0EVLds4RBkInvaUZjVL7h3oJ5I19GugPTzlyVyYtkf1ej6LNttkagqHgMck87UQGvCbwDX9ECTngffwQPYZlZKnthW0DlkFGgHN8T9uqEpl-3ki50gTa6gC0Q16mEeDRKZe7_g5Sw52OjMsfWxmBBWWMSHzlQKKAIKMKKaD6Td0O_zpiXXp7Fyl7z_iESvCpOAUAIKnyJyF_Y0UYktEmw=w2066-h1377-no' + ); } - }) + }); it('image, ' + largeData.length + ' bytes x 10,000', () => { for (let i = 0; i < 10000; i++) { - const match = (largeData + 'https://example.com/img.jpeg').match(linksRe.image()) - assert(match, 'no match') - assert(match[0] === 'https://example.com/img.jpeg', 'no match') + const match = (largeData + 'https://example.com/img.jpeg').match( + linksRe.image() + ); + assert(match, 'no match'); + assert(match[0] === 'https://example.com/img.jpeg', 'no match'); } - }) + }); it('remote, ' + largeData.length + ' bytes x 10,000', () => { for (let i = 0; i < 10000; i++) { - const match = (largeData + 'https://example.com').match(linksRe.remote()) - assert(match, 'no match') - assert(match[0] === 'https://example.com', 'no match') + const match = (largeData + 'https://example.com').match( + linksRe.remote() + ); + assert(match, 'no match'); + assert(match[0] === 'https://example.com', 'no match'); } - }) + }); it('youTube', () => { - match(linksRe.youTube(), 'https://youtu.be/xG7ajrbj4zs?t=7s') - match(linksRe.youTube(), 'https://www.youtube.com/watch?v=xG7ajrbj4zs&t=14s') - match(linksRe.youTube(), 'https://www.youtube.com/watch?v=xG7ajrbj4zs&feature=youtu.be&t=14s') - }) + match(linksRe.youTube(), 'https://youtu.be/xG7ajrbj4zs?t=7s'); + match( + linksRe.youTube(), + 'https://www.youtube.com/watch?v=xG7ajrbj4zs&t=14s' + ); + match( + linksRe.youTube(), + 'https://www.youtube.com/watch?v=xG7ajrbj4zs&feature=youtu.be&t=14s' + ); + }); it('youTubeId', () => { - match(links.youTubeId, 'https://youtu.be/xG7ajrbj4zs?t=7s', 'xG7ajrbj4zs', 1) - match(links.youTubeId, 'https://www.youtube.com/watch?v=xG7ajrbj4zs&t=14s', 'xG7ajrbj4zs', 1) - match(links.youTubeId, 'https://www.youtube.com/watch?v=xG7ajrbj4zs&feature=youtu.be&t=14s', 'xG7ajrbj4zs', 1) - }) -}) + match( + links.youTubeId, + 'https://youtu.be/xG7ajrbj4zs?t=7s', + 'xG7ajrbj4zs', + 1 + ); + match( + links.youTubeId, + 'https://www.youtube.com/watch?v=xG7ajrbj4zs&t=14s', + 'xG7ajrbj4zs', + 1 + ); + match( + links.youTubeId, + 'https://www.youtube.com/watch?v=xG7ajrbj4zs&feature=youtu.be&t=14s', + 'xG7ajrbj4zs', + 1 + ); + }); +}); -const match = (...args) => compare(true, ...args) -const matchNot = (...args) => compare(false, ...args) +const match = (...args) => compare(true, ...args); +const matchNot = (...args) => compare(false, ...args); const compare = (matching, re, input, output = input, pos = 0) => { if (Array.isArray(input)) { for (let i = 0; i < input.length; i++) - compare(matching, re, input[i], output[i]) - return + compare(matching, re, input[i], output[i]); + return; } // console.log('compare, input', input) // console.log('compare, output', output) - const m = input.match(re) - if(matching) { - assert(m, `No match --> ${input} --> output ${output} --> using ${re.toString()}`) + const m = input.match(re); + if (matching) { + assert( + m, + `No match --> ${input} --> output ${ + output + } --> using ${re.toString()}` + ); // console.log('m', m) - assert.equal(m[pos], output, `Unmatched ${m[pos]} --> input ${input} --> output ${output} --> using ${re.toString()}`) + assert.equal( + m[pos], + output, + `Unmatched ${m[pos]} --> input ${input} --> output ${ + output + } --> using ${re.toString()}` + ); } else { - assert(!m, `False match --> input ${input} --> output ${output} --> using ${re.toString()}`) + assert( + !m, + `False match --> input ${input} --> output ${ + output + } --> using ${re.toString()}` + ); } -} +}; diff --git a/src/app/utils/MarketClasses.js b/src/app/utils/MarketClasses.js index 678eb10d0..e103eeb32 100644 --- a/src/app/utils/MarketClasses.js +++ b/src/app/utils/MarketClasses.js @@ -1,12 +1,15 @@ -import {roundDown, roundUp} from "./MarketUtils"; -import { LIQUID_TICKER, DEBT_TICKER } from 'app/client_config' +import { roundDown, roundUp } from './MarketUtils'; +import { LIQUID_TICKER, DEBT_TICKER } from 'app/client_config'; const precision = 1000; class Order { constructor(order, side) { this.side = side; this.price = parseFloat(order.real_price); - this.price = side === 'asks' ? roundUp(this.price, 6) : Math.max(roundDown(this.price, 6), 0.000001); + this.price = + side === 'asks' + ? roundUp(this.price, 6) + : Math.max(roundDown(this.price, 6), 0.000001); this.stringPrice = this.price.toFixed(6); this.steem = parseInt(order.steem, 10); this.sbd = parseInt(order.sbd, 10); @@ -38,12 +41,15 @@ class Order { } add(order) { - return new Order({ - real_price: this.price, - steem: this.steem + order.steem, - sbd: this.sbd + order.sbd, - date: this.date - }, this.type); + return new Order( + { + real_price: this.price, + steem: this.steem + order.steem, + sbd: this.sbd + order.sbd, + date: this.date, + }, + this.type + ); } equals(order) { @@ -56,26 +62,34 @@ class Order { } class TradeHistory { - constructor(fill) { // Norm date (FF bug) var zdate = fill.date; - if(!/Z$/.test(zdate)) - zdate = zdate + 'Z' + if (!/Z$/.test(zdate)) zdate = zdate + 'Z'; this.date = new Date(zdate); - this.type = fill.current_pays.indexOf(DEBT_TICKER) !== -1 ? "bid" : "ask"; - this.color = this.type == "bid" ? "buy-color" : "sell-color"; - if (this.type === "bid") { - this.sbd = parseFloat(fill.current_pays.split(" " + DEBT_TICKER)[0]); - this.steem = parseFloat(fill.open_pays.split(" " + LIQUID_TICKER)[0]); + this.type = + fill.current_pays.indexOf(DEBT_TICKER) !== -1 ? 'bid' : 'ask'; + this.color = this.type == 'bid' ? 'buy-color' : 'sell-color'; + if (this.type === 'bid') { + this.sbd = parseFloat( + fill.current_pays.split(' ' + DEBT_TICKER)[0] + ); + this.steem = parseFloat( + fill.open_pays.split(' ' + LIQUID_TICKER)[0] + ); } else { - this.sbd = parseFloat(fill.open_pays.split(" " + DEBT_TICKER)[0]); - this.steem = parseFloat(fill.current_pays.split(" " + LIQUID_TICKER)[0]); + this.sbd = parseFloat(fill.open_pays.split(' ' + DEBT_TICKER)[0]); + this.steem = parseFloat( + fill.current_pays.split(' ' + LIQUID_TICKER)[0] + ); } this.price = this.sbd / this.steem; - this.price = this.type === 'ask' ? roundUp(this.price, 6) : Math.max(roundDown(this.price, 6), 0.000001); + this.price = + this.type === 'ask' + ? roundUp(this.price, 6) + : Math.max(roundDown(this.price, 6), 0.000001); this.stringPrice = this.price.toFixed(6); } @@ -114,5 +128,5 @@ class TradeHistory { module.exports = { Order, - TradeHistory -} + TradeHistory, +}; diff --git a/src/app/utils/MarketUtils.js b/src/app/utils/MarketUtils.js index 6b6e9215d..81f4c7b19 100644 --- a/src/app/utils/MarketUtils.js +++ b/src/app/utils/MarketUtils.js @@ -1,24 +1,24 @@ function roundUp(num, precision) { - let satoshis = parseFloat(num) * Math.pow(10, precision) + let satoshis = parseFloat(num) * Math.pow(10, precision); // Attempt to correct floating point: 1.0001 satoshis should not round up. - satoshis = satoshis - 0.0001 + satoshis = satoshis - 0.0001; // Round up, restore precision - return Math.ceil(satoshis) / Math.pow(10, precision) + return Math.ceil(satoshis) / Math.pow(10, precision); } function roundDown(num, precision) { - let satoshis = parseFloat(num) * Math.pow(10, precision) + let satoshis = parseFloat(num) * Math.pow(10, precision); // Attempt to correct floating point: 1.9999 satoshis should not round down. - satoshis = satoshis + 0.0001 + satoshis = satoshis + 0.0001; // Round down, restore precision - return Math.floor(satoshis) / Math.pow(10, precision) + return Math.floor(satoshis) / Math.pow(10, precision); } module.exports = { roundUp, - roundDown -} + roundDown, +}; diff --git a/src/app/utils/NormalizeProfile.js b/src/app/utils/NormalizeProfile.js index 1fef9b06c..d9f3fbfc2 100644 --- a/src/app/utils/NormalizeProfile.js +++ b/src/app/utils/NormalizeProfile.js @@ -1,61 +1,76 @@ -import linksRe from 'app/utils/Links' +import linksRe from 'app/utils/Links'; function truncate(str, len) { - if(str) { - str = str.trim() - if(str.length > len) { - str = str.substring(0, len - 1) + '...' + if (str) { + str = str.trim(); + if (str.length > len) { + str = str.substring(0, len - 1) + '...'; } } - return str + return str; } /** * Enforce profile data length & format standards. */ export default function normalizeProfile(account) { - - if(! account) return {} + if (!account) return {}; // Parse let profile = {}; - if(account.json_metadata) { + if (account.json_metadata) { try { const md = JSON.parse(account.json_metadata); - if(md.profile) { + if (md.profile) { profile = md.profile; } - if(!(typeof profile == 'object')) { - console.error('Expecting object in account.json_metadata.profile:', profile); + if (!(typeof profile == 'object')) { + console.error( + 'Expecting object in account.json_metadata.profile:', + profile + ); profile = {}; } } catch (e) { - console.error('Invalid json metadata string', account.json_metadata, 'in account', account.name); + console.error( + 'Invalid json metadata string', + account.json_metadata, + 'in account', + account.name + ); } } // Read & normalize - let {name, about, location, website, profile_image, cover_image} = profile + let { + name, + about, + location, + website, + profile_image, + cover_image, + } = profile; - name = truncate(name, 20) - about = truncate(about, 160) - location = truncate(location, 30) + name = truncate(name, 20); + about = truncate(about, 160); + location = truncate(location, 30); - if(/^@/.test(name)) name = null; - if(website && website.length > 100) website = null; - if (website && website.indexOf("http") === -1) { + if (/^@/.test(name)) name = null; + if (website && website.length > 100) website = null; + if (website && website.indexOf('http') === -1) { website = 'http://' + website; } - if(website) { + if (website) { // enforce that the url regex matches, and fully - const m = website.match(linksRe.any) - if(!m || m[0] !== website) { + const m = website.match(linksRe.any); + if (!m || m[0] !== website) { website = null; } } - if(profile_image && !/^https?:\/\//.test(profile_image)) profile_image = null; - if(cover_image && !/^https?:\/\//.test(cover_image)) cover_image = null; + if (profile_image && !/^https?:\/\//.test(profile_image)) + profile_image = null; + if (cover_image && !/^https?:\/\//.test(cover_image)) cover_image = null; return { name, diff --git a/src/app/utils/Notifications.js b/src/app/utils/Notifications.js index d3f876140..0a77c887d 100644 --- a/src/app/utils/Notifications.js +++ b/src/app/utils/Notifications.js @@ -1,7 +1,23 @@ -export const NTYPES = ['total', 'feed', 'reward', 'send', 'mention', 'follow', 'vote', 'comment_reply', 'post_reply', 'account_update', 'message', 'receive']; +export const NTYPES = [ + 'total', + 'feed', + 'reward', + 'send', + 'mention', + 'follow', + 'vote', + 'comment_reply', + 'post_reply', + 'account_update', + 'message', + 'receive', +]; export function notificationsArrayToMap(data) { - const notifications = data && data.length ? (data.length === 1 ? data[0].slice(1) : data) : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + const notifications = + data && data.length + ? data.length === 1 ? data[0].slice(1) : data + : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; return notifications.reduce((result, n, i) => { result[NTYPES[i]] = n; return result; diff --git a/src/app/utils/ParsersAndFormatters.js b/src/app/utils/ParsersAndFormatters.js index 98e873724..ea8f81e74 100644 --- a/src/app/utils/ParsersAndFormatters.js +++ b/src/app/utils/ParsersAndFormatters.js @@ -23,8 +23,18 @@ export function formatDecimal(value, decPlaces = 2, truncate0s = true) { i = parseInt(abs_value.toFixed(decPlaces), 10) + ''; j = i.length; j = i.length > 3 ? j % 3 : 0; - const decPart = (decPlaces ? decSeparator + Math.abs(abs_value - i).toFixed(decPlaces).slice(2) : ''); - return [sign + (j ? i.substr(0, j) + thouSeparator : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thouSeparator), decPart]; + const decPart = decPlaces + ? decSeparator + + Math.abs(abs_value - i) + .toFixed(decPlaces) + .slice(2) + : ''; + return [ + sign + + (j ? i.substr(0, j) + thouSeparator : '') + + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thouSeparator), + decPart, + ]; } export function parsePayoutAmount(amount) { @@ -38,59 +48,71 @@ export function parsePayoutAmount(amount) { */ function log10(str) { const leadingDigits = parseInt(str.substring(0, 4)); - const log = Math.log(leadingDigits) / Math.LN10 + 0.00000001 + const log = Math.log(leadingDigits) / Math.LN10 + 0.00000001; const n = str.length - 1; return n + (log - parseInt(log)); } export const repLog10 = rep2 => { - if(rep2 == null) return rep2 - let rep = String(rep2) - const neg = rep.charAt(0) === '-' - rep = neg ? rep.substring(1) : rep + if (rep2 == null) return rep2; + let rep = String(rep2); + const neg = rep.charAt(0) === '-'; + rep = neg ? rep.substring(1) : rep; - let out = log10(rep) - if(isNaN(out)) out = 0 + let out = log10(rep); + if (isNaN(out)) out = 0; out = Math.max(out - 9, 0); // @ -9, $0.50 earned is approx magnitude 1 - out = (neg ? -1 : 1) * out - out = (out * 9) + 25 // 9 points per magnitude. center at 25 + out = (neg ? -1 : 1) * out; + out = out * 9 + 25; // 9 points per magnitude. center at 25 // base-line 0 to darken and < 0 to auto hide (grep rephide) - out = parseInt(out) - return out -} + out = parseInt(out); + return out; +}; export function countDecimals(amount) { - if(amount == null) return amount - amount = String(amount).match(/[\d\.]+/g).join('') // just dots and digits - const parts = amount.split('.') - return parts.length > 2 ? undefined : parts.length === 1 ? 0 : parts[1].length + if (amount == null) return amount; + amount = String(amount) + .match(/[\d\.]+/g) + .join(''); // just dots and digits + const parts = amount.split('.'); + return parts.length > 2 + ? undefined + : parts.length === 1 ? 0 : parts[1].length; } // this function searches for right translation of provided error (usually from back-end) export function translateError(string) { - if (typeof(string) != 'string') return string; + if (typeof string != 'string') return string; switch (string) { case 'Account not found': - return tt('g.account_not_found') + return tt('g.account_not_found'); case 'Incorrect Password': - return tt('g.incorrect_password') + return tt('g.incorrect_password'); case 'Username does not exist': - return tt('g.username_does_not_exist') + return tt('g.username_does_not_exist'); case 'Account name should be longer.': - return tt('g.account_name_should_be_longer') + return tt('g.account_name_should_be_longer'); case 'Account name should be shorter.': - return tt('g.account_name_should_be_shorter') + return tt('g.account_name_should_be_shorter'); case 'Account name should start with a letter.': - return tt('g.account_name_should_start_with_a_letter') + return tt('g.account_name_should_start_with_a_letter'); case 'Account name should have only letters, digits, or dashes.': - return tt('g.account_name_should_have_only_letters_digits_or_dashes') + return tt( + 'g.account_name_should_have_only_letters_digits_or_dashes' + ); case 'vote currently exists, user must be indicate a desire to reject witness': - return tt('g.vote_currently_exists_user_must_be_indicate_a_to_reject_witness') + return tt( + 'g.vote_currently_exists_user_must_be_indicate_a_to_reject_witness' + ); case 'Only one Steem account allowed per IP address every 10 minutes': - return tt('g.only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes') + return tt( + 'g.only_one_APP_NAME_account_allowed_per_ip_address_every_10_minutes' + ); case 'Cannot increase reward of post within the last minute before payout': - return tt('g.cannot_increase_reward_of_post_within_the_last_minute_before_payout') + return tt( + 'g.cannot_increase_reward_of_post_within_the_last_minute_before_payout' + ); default: - return string + return string; } } diff --git a/src/app/utils/ProxifyUrl.js b/src/app/utils/ProxifyUrl.js index 9f5f6afd3..5f5477934 100644 --- a/src/app/utils/ProxifyUrl.js +++ b/src/app/utils/ProxifyUrl.js @@ -32,12 +32,13 @@ export default (url, dimensions = false) => { if (dimensions && $STM_Config && $STM_Config.img_proxy_prefix) { let dims = dimensions + '/'; if (typeof dimensions !== 'string') { - dims = (proxyList) ? proxyList.shift().match(/([0-9]+x[0-9]+)\//g)[0] : NATURAL_SIZE; + dims = proxyList + ? proxyList.shift().match(/([0-9]+x[0-9]+)\//g)[0] + : NATURAL_SIZE; } if (NATURAL_SIZE !== dims || !rProxyDomain.test(respUrl)) { return $STM_Config.img_proxy_prefix + dims + respUrl; } } return respUrl; -} - +}; diff --git a/src/app/utils/ProxifyUrl.test.js b/src/app/utils/ProxifyUrl.test.js index e1f8db77d..a0988da96 100644 --- a/src/app/utils/ProxifyUrl.test.js +++ b/src/app/utils/ProxifyUrl.test.js @@ -1,61 +1,155 @@ /*global describe, global, before:false, it*/ -import assert from 'assert' -import proxifyImageUrl from './ProxifyUrl' +import assert from 'assert'; +import proxifyImageUrl from './ProxifyUrl'; describe('ProxifyUrl', () => { before(() => { - global.$STM_Config = {img_proxy_prefix: 'https://steemitimages.com/'}; + global.$STM_Config = { img_proxy_prefix: 'https://steemitimages.com/' }; }); it('naked URL', () => { - testCase('https://example.com/img.png', '100x200', 'https://steemitimages.com/100x200/https://example.com/img.png') - testCase('https://example.com/img.png', '0x0', 'https://steemitimages.com/0x0/https://example.com/img.png') - testCase('https://example.com/img.png', true, 'https://steemitimages.com/0x0/https://example.com/img.png') - testCase('https://example.com/img.png', false, 'https://example.com/img.png') - }) + testCase( + 'https://example.com/img.png', + '100x200', + 'https://steemitimages.com/100x200/https://example.com/img.png' + ); + testCase( + 'https://example.com/img.png', + '0x0', + 'https://steemitimages.com/0x0/https://example.com/img.png' + ); + testCase( + 'https://example.com/img.png', + true, + 'https://steemitimages.com/0x0/https://example.com/img.png' + ); + testCase( + 'https://example.com/img.png', + false, + 'https://example.com/img.png' + ); + }); it('naked steemit hosted URL', () => { - testCase('https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', '256x512', 'https://steemitimages.com/256x512/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg') - testCase('https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', false, 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg') - }) + testCase( + 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', + '256x512', + 'https://steemitimages.com/256x512/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg' + ); + testCase( + 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', + false, + 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg' + ); + }); it('proxied steemit hosted URL', () => { - testCase('https://steemitimages.com/0x0/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', '256x512', 'https://steemitimages.com/256x512/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg') - testCase('https://steemitimages.com/256x512/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', false, 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg') - }) + testCase( + 'https://steemitimages.com/0x0/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', + '256x512', + 'https://steemitimages.com/256x512/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg' + ); + testCase( + 'https://steemitimages.com/256x512/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', + false, + 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg' + ); + }); it('proxied URL', () => { - testCase('https://steemitimages.com/0x0/https://example.com/img.png', '100x200', 'https://steemitimages.com/100x200/https://example.com/img.png') - testCase('https://steemitimages.com/256x512/https://peopledotcom.files.wordpress.com/2017/09/grumpy-harvey-cat.jpg?w=2000', '100x200', 'https://steemitimages.com/100x200/https://peopledotcom.files.wordpress.com/2017/09/grumpy-harvey-cat.jpg?w=2000') - testCase('https://steemitimages.com/0x0/https://example.com/img.png', false, 'https://example.com/img.png') - }) + testCase( + 'https://steemitimages.com/0x0/https://example.com/img.png', + '100x200', + 'https://steemitimages.com/100x200/https://example.com/img.png' + ); + testCase( + 'https://steemitimages.com/256x512/https://peopledotcom.files.wordpress.com/2017/09/grumpy-harvey-cat.jpg?w=2000', + '100x200', + 'https://steemitimages.com/100x200/https://peopledotcom.files.wordpress.com/2017/09/grumpy-harvey-cat.jpg?w=2000' + ); + testCase( + 'https://steemitimages.com/0x0/https://example.com/img.png', + false, + 'https://example.com/img.png' + ); + }); it('double-proxied URL', () => { - testCase('https://steemitimages.com/0x0/https://steemitimages.com/0x0/https://example.com/img.png', '100x200', 'https://steemitimages.com/100x200/https://example.com/img.png') - testCase('https://steemitimages.com/0x0/https://steemitimages.com/256x512/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', false, 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg') - }) + testCase( + 'https://steemitimages.com/0x0/https://steemitimages.com/0x0/https://example.com/img.png', + '100x200', + 'https://steemitimages.com/100x200/https://example.com/img.png' + ); + testCase( + 'https://steemitimages.com/0x0/https://steemitimages.com/256x512/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', + false, + 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg' + ); + }); it('preserve dimensions - single-proxied URL', () => { //simple preservation - testCase('https://steemitdevimages.com/100x200/https://example.com/img.png', true, 'https://steemitimages.com/100x200/https://example.com/img.png') - testCase('https://steemitdevimages.com/1001x2001/https://example.com/img.png', true, 'https://steemitimages.com/1001x2001/https://example.com/img.png') - }) + testCase( + 'https://steemitdevimages.com/100x200/https://example.com/img.png', + true, + 'https://steemitimages.com/100x200/https://example.com/img.png' + ); + testCase( + 'https://steemitdevimages.com/1001x2001/https://example.com/img.png', + true, + 'https://steemitimages.com/1001x2001/https://example.com/img.png' + ); + }); it('preserve dimensions - double-proxied URL', () => { //simple preservation at a 2 nesting level //foreign domain - testCase('https://steemitimages.com/100x200/https://steemitimages.com/0x0/https://example.com/img.png', true, 'https://steemitimages.com/100x200/https://example.com/img.png') - testCase('https://steemitdevimages.com/1001x2001/https://steemitimages.com/0x0/https://example.com/img.png', true, 'https://steemitimages.com/1001x2001/https://example.com/img.png') + testCase( + 'https://steemitimages.com/100x200/https://steemitimages.com/0x0/https://example.com/img.png', + true, + 'https://steemitimages.com/100x200/https://example.com/img.png' + ); + testCase( + 'https://steemitdevimages.com/1001x2001/https://steemitimages.com/0x0/https://example.com/img.png', + true, + 'https://steemitimages.com/1001x2001/https://example.com/img.png' + ); //steemit domain - testCase('https://steemitdevimages.com/1001x2001/https://steemitimages.com/0x0/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', true, 'https://steemitimages.com/1001x2001/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg') - }) + testCase( + 'https://steemitdevimages.com/1001x2001/https://steemitimages.com/0x0/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', + true, + 'https://steemitimages.com/1001x2001/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg' + ); + }); it('preserve dimensions - strip proxies & dimensions when appropriate', () => { //simple preservation at a 2 nesting level //steemit domain - testCase('https://steemitimages.com/0x0/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', true, 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg') + testCase( + 'https://steemitimages.com/0x0/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', + true, + 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg' + ); //foreign domain - testCase('https://steemitimages.com/0x0/https://example.com/img.png', true, 'https://steemitimages.com/0x0/https://example.com/img.png') + testCase( + 'https://steemitimages.com/0x0/https://example.com/img.png', + true, + 'https://steemitimages.com/0x0/https://example.com/img.png' + ); //case where last is natural sizing, assumes natural sizing - straight to direct steemit file url - testCase('https://steemitimages.com/0x0/https://steemitimages.com/100x100/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', true, 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg') + testCase( + 'https://steemitimages.com/0x0/https://steemitimages.com/100x100/https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg', + true, + 'https://steemitimages.com/DQmaJe2Tt5kmVUaFhse1KTEr4N1g9piMgD3YjPEQhkZi3HR/30day-positivity-challenge.jpg' + ); //case where last is natural sizing, assumes natural sizing - straight to direct steemit /0x0/ domain host url - testCase('https://steemitimages.com/0x0/https://steemitimages.com/100x100/https://example.com/img.png', true, 'https://steemitimages.com/0x0/https://example.com/img.png') - }) -}) + testCase( + 'https://steemitimages.com/0x0/https://steemitimages.com/100x100/https://example.com/img.png', + true, + 'https://steemitimages.com/0x0/https://example.com/img.png' + ); + }); +}); const testCase = (inputUrl, outputDims, expectedUrl) => { const outputUrl = proxifyImageUrl(inputUrl, outputDims); - assert.equal(outputUrl, expectedUrl, `(${inputUrl}, ${outputDims}) should return ${expectedUrl}. output was ${outputUrl}`) -} + assert.equal( + outputUrl, + expectedUrl, + `(${inputUrl}, ${outputDims}) should return ${ + expectedUrl + }. output was ${outputUrl}` + ); +}; diff --git a/src/app/utils/ReactForm.js b/src/app/utils/ReactForm.js index fecc7a1f9..4964b0d8c 100644 --- a/src/app/utils/ReactForm.js +++ b/src/app/utils/ReactForm.js @@ -5,165 +5,171 @@ @arg {object} initialValues required for checkboxes {save: false, ...} @arg {function} validation - values => ({ username: ! values.username ? 'Required' : null, ... }) */ -export default function reactForm({name, instance, fields, initialValues, validation = () => {}}) { - if(typeof instance !== 'object') throw new TypeError('instance is a required object') - if(!Array.isArray(fields)) throw new TypeError('fields is a required array') - if(typeof initialValues !== 'object') throw new TypeError('initialValues is a required object') +export default function reactForm({ + name, + instance, + fields, + initialValues, + validation = () => {}, +}) { + if (typeof instance !== 'object') + throw new TypeError('instance is a required object'); + if (!Array.isArray(fields)) + throw new TypeError('fields is a required array'); + if (typeof initialValues !== 'object') + throw new TypeError('initialValues is a required object'); // Give API users access to this.props, this.state, this.etc.. - validation = validation.bind(instance) + validation = validation.bind(instance); - const formState = instance.state = instance.state || {} + const formState = (instance.state = instance.state || {}); formState[name] = { // validate: () => setFormState(instance, fields, validation), handleSubmit: submitCallback => event => { - event.preventDefault() - const {valid} = setFormState(name, instance, fields, validation) - if(!valid) return - const data = getData(fields, instance.state) - let formValid = true - const fs = instance.state[name] || {} - fs.submitting = true + event.preventDefault(); + const { valid } = setFormState(name, instance, fields, validation); + if (!valid) return; + const data = getData(fields, instance.state); + let formValid = true; + const fs = instance.state[name] || {}; + fs.submitting = true; // User can call this function upon successful submission const updateInitialValues = () => { - setInitialValuesFromForm(name, instance, fields, initialValues) - formState[name].resetForm() - } - - instance.setState( - {[name]: fs}, - () => { - // TODO, support promise ret - const ret = submitCallback({data, event, updateInitialValues}) || {} - // Look for field level errors - for(const fieldName of Object.keys(ret)) { - const error = ret[fieldName] - if(!error) continue - const value = instance.state[fieldName] || {} - value.error = error - value.touched = true - if(error) formValid = false - instance.setState({[fieldName]: value}) - } - fs.submitting = false - fs.valid = formValid - instance.setState({[name]: fs}) + setInitialValuesFromForm(name, instance, fields, initialValues); + formState[name].resetForm(); + }; + + instance.setState({ [name]: fs }, () => { + // TODO, support promise ret + const ret = + submitCallback({ data, event, updateInitialValues }) || {}; + // Look for field level errors + for (const fieldName of Object.keys(ret)) { + const error = ret[fieldName]; + if (!error) continue; + const value = instance.state[fieldName] || {}; + value.error = error; + value.touched = true; + if (error) formValid = false; + instance.setState({ [fieldName]: value }); } - ) + fs.submitting = false; + fs.valid = formValid; + instance.setState({ [name]: fs }); + }); }, resetForm: () => { - for(const field of fields) { - const fieldName = n(field) - const f = instance.state[fieldName] - const def = initialValues[fieldName] - f.props.onChange(def) + for (const field of fields) { + const fieldName = n(field); + const f = instance.state[fieldName]; + const def = initialValues[fieldName]; + f.props.onChange(def); } }, clearForm: () => { - for(const field of fields) { - const fieldName = n(field) - const f = instance.state[fieldName] - f.props.onChange() + for (const field of fields) { + const fieldName = n(field); + const f = instance.state[fieldName]; + f.props.onChange(); } }, - } + }; - for(const field of fields) { - const fieldName = n(field) - const fieldType = t(field) + for (const field of fields) { + const fieldName = n(field); + const fieldType = t(field); - const fs = formState[fieldName] = { + const fs = (formState[fieldName] = { value: null, error: null, touched: false, - } + }); // Caution: fs.props is expanded , so only add valid props for the component - fs.props = {name: fieldName} + fs.props = { name: fieldName }; { - const initialValue = initialValues[fieldName] - if(fieldType === 'checked') { - fs.value = toString(initialValue) - fs.props.checked = toBoolean(initialValue) - } else if(fieldType === 'selected') { - fs.props.selected = toString(initialValue) - fs.value = fs.props.selected + const initialValue = initialValues[fieldName]; + if (fieldType === 'checked') { + fs.value = toString(initialValue); + fs.props.checked = toBoolean(initialValue); + } else if (fieldType === 'selected') { + fs.props.selected = toString(initialValue); + fs.value = fs.props.selected; } else { - fs.props.value = toString(initialValue) - fs.value = fs.props.value + fs.props.value = toString(initialValue); + fs.value = fs.props.value; } } fs.props.onChange = e => { - const value = e && e.target ? e.target.value : e // API may pass value directly - const v = {...(instance.state[fieldName] || {})} - const initialValue = initialValues[fieldName] - - if(fieldType === 'checked') { - v.touched = toString(value) !== toString(initialValue) - v.value = v.props.checked = toBoolean(value) - v.value = value - } else if(fieldType === 'selected') { - v.touched = toString(value) !== toString(initialValue) - v.value = v.props.selected = toString(value) + const value = e && e.target ? e.target.value : e; // API may pass value directly + const v = { ...(instance.state[fieldName] || {}) }; + const initialValue = initialValues[fieldName]; + + if (fieldType === 'checked') { + v.touched = toString(value) !== toString(initialValue); + v.value = v.props.checked = toBoolean(value); + v.value = value; + } else if (fieldType === 'selected') { + v.touched = toString(value) !== toString(initialValue); + v.value = v.props.selected = toString(value); } else { - v.touched = toString(value) !== toString(initialValue) - v.value = v.props.value = toString(value) + v.touched = toString(value) !== toString(initialValue); + v.value = v.props.value = toString(value); } - instance.setState( - {[fieldName]: v}, - () => {setFormState(name, instance, fields, validation)} - ) - } + instance.setState({ [fieldName]: v }, () => { + setFormState(name, instance, fields, validation); + }); + }; fs.props.onBlur = () => { // Some errors are better shown only after blur === true - const v = {...(instance.state[fieldName] || {})} - v.blur = true - instance.setState({[fieldName]: v}) - } + const v = { ...(instance.state[fieldName] || {}) }; + v.blur = true; + instance.setState({ [fieldName]: v }); + }; } } function setFormState(name, instance, fields, validation) { - let formValid = true - let formTouched = false - const v = validation(getData(fields, instance.state)) - for(const field of fields) { - const fieldName = n(field) - const validate = v[fieldName] - const error = validate ? validate : null - const value = {...(instance.state[fieldName] || {})} - value.error = error - formTouched = formTouched || value.touched - if(error) formValid = false - instance.setState({[fieldName]: value}) + let formValid = true; + let formTouched = false; + const v = validation(getData(fields, instance.state)); + for (const field of fields) { + const fieldName = n(field); + const validate = v[fieldName]; + const error = validate ? validate : null; + const value = { ...(instance.state[fieldName] || {}) }; + value.error = error; + formTouched = formTouched || value.touched; + if (error) formValid = false; + instance.setState({ [fieldName]: value }); } - const fs = {...(instance.state[name] || {})} - fs.valid = formValid - fs.touched = formTouched - instance.setState({[name]: fs}) - return fs + const fs = { ...(instance.state[name] || {}) }; + fs.valid = formValid; + fs.touched = formTouched; + instance.setState({ [name]: fs }); + return fs; } function setInitialValuesFromForm(name, instance, fields, initialValues) { - const data = getData(fields, instance.state) - for(const field of fields) { - const fieldName = n(field) - initialValues[fieldName] = data[fieldName] + const data = getData(fields, instance.state); + for (const field of fields) { + const fieldName = n(field); + initialValues[fieldName] = data[fieldName]; } } function getData(fields, state) { - const data = {} - for(const field of fields) { - const fieldName = n(field) - data[fieldName] = state[fieldName].value + const data = {}; + for (const field of fields) { + const fieldName = n(field); + data[fieldName] = state[fieldName].value; } - return data + return data; } /* @@ -176,18 +182,21 @@ function getData(fields, state) { @return {string} type */ function t(field) { - const [, type = 'string'] = field.split(':') - return type + const [, type = 'string'] = field.split(':'); + return type; } /** @return {string} name */ function n(field) { - const [name] = field.split(':') - return name + const [name] = field.split(':'); + return name; } -const hasValue = v => v == null ? false : (typeof v === 'string' ? v.trim() : v) === '' ? false : true -const toString = v => hasValue(v) ? v : '' -const toBoolean = v => hasValue(v) ? JSON.parse(v) : '' +const hasValue = v => + v == null + ? false + : (typeof v === 'string' ? v.trim() : v) === '' ? false : true; +const toString = v => (hasValue(v) ? v : ''); +const toBoolean = v => (hasValue(v) ? JSON.parse(v) : ''); diff --git a/src/app/utils/ReduxForms.js b/src/app/utils/ReduxForms.js index 8e8e2a0bd..54a788e9e 100644 --- a/src/app/utils/ReduxForms.js +++ b/src/app/utils/ReduxForms.js @@ -1,9 +1,27 @@ export const cleanReduxInput = i => { // Remove all props that don't belong. Triggers React warnings. - const {name, placeholder, label, value, checked, onChange, onBlur, onFocus} = i - const ret = {name, placeholder, label, value, checked, onChange, onBlur, onFocus} - if(ret.value == null) delete ret.value - if(ret.label == null) delete ret.label - if(ret.type == null) delete ret.type - return ret -} + const { + name, + placeholder, + label, + value, + checked, + onChange, + onBlur, + onFocus, + } = i; + const ret = { + name, + placeholder, + label, + value, + checked, + onChange, + onBlur, + onFocus, + }; + if (ret.value == null) delete ret.value; + if (ret.label == null) delete ret.label; + if (ret.type == null) delete ret.type; + return ret; +}; diff --git a/src/app/utils/RegisterServiceWorker.js b/src/app/utils/RegisterServiceWorker.js index 4da112e26..f5b407222 100644 --- a/src/app/utils/RegisterServiceWorker.js +++ b/src/app/utils/RegisterServiceWorker.js @@ -1,34 +1,49 @@ export default function registerServiceWorker() { if (!navigator.serviceWorker) return Promise.resolve(false); - return navigator.serviceWorker.register('/service-worker.js', {scope: '/'}) - .then(function (registration) { - navigator.serviceWorker.ready.catch(e => console.error('-- registerServiceWorker error -->', e)); - return navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) { + return navigator.serviceWorker + .register('/service-worker.js', { scope: '/' }) + .then(function(registration) { + navigator.serviceWorker.ready.catch(e => + console.error('-- registerServiceWorker error -->', e) + ); + return navigator.serviceWorker.ready.then(function( + serviceWorkerRegistration + ) { let subscription = serviceWorkerRegistration.pushManager.getSubscription(); - return subscription.then(function (subscription) { - if (subscription) { - return subscription; - } - return serviceWorkerRegistration.pushManager.subscribe({ - userVisibleOnly: true - }); + return subscription.then(function(subscription) { + if (subscription) { + return subscription; + } + return serviceWorkerRegistration.pushManager.subscribe({ + userVisibleOnly: true, }); + }); }); - }).then(function (subscription) { - const rawKey = subscription.getKey ? subscription.getKey('p256dh') : ''; - const key = rawKey ? - btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) : - ''; - const rawAuthSecret = subscription.getKey ? subscription.getKey('auth') : ''; - const authSecret = rawAuthSecret ? - btoa(String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))) : - ''; + }) + .then(function(subscription) { + const rawKey = subscription.getKey + ? subscription.getKey('p256dh') + : ''; + const key = rawKey + ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) + : ''; + const rawAuthSecret = subscription.getKey + ? subscription.getKey('auth') + : ''; + const authSecret = rawAuthSecret + ? btoa( + String.fromCharCode.apply( + null, + new Uint8Array(rawAuthSecret) + ) + ) + : ''; return { endpoint: subscription.endpoint, keys: { p256dh: key, - auth: authSecret - } + auth: authSecret, + }, }; }); } diff --git a/src/app/utils/RemarkablePlugin.js b/src/app/utils/RemarkablePlugin.js index 61bceda79..9c6befefe 100644 --- a/src/app/utils/RemarkablePlugin.js +++ b/src/app/utils/RemarkablePlugin.js @@ -51,7 +51,7 @@ console.log('renderedText', renderedText) // text: (tokens, i, options, env, renderer) => { // if(tagRules.youtubeId) // return `youtube:${tagRules.youtubeId}${tagRules.youtubeTime ? ',' + tagRules.youtubeTime : ''}` -// +// // if(tagLinkOpen) // return renderer.rules.text(tokens, i, options, env, renderer) // let content = tokens[i].content @@ -77,7 +77,7 @@ console.log('renderedText', renderedText) // // unescapted ipfs links (temp, until the reply editor categorizes the image) // //if(config.ipfs_prefix) // // content = content.replace(linksRe.ipfsPrefix, config.ipfs_prefix) -// +// // return content // }, // link_close: (tokens, i, options, env, renderer) => { @@ -93,7 +93,7 @@ console.log('renderedText', renderedText) // let hashtags = new Set() // let usertags = new Set() // let tagLinkOpen -// +// // export const imageLinks = { // done: () => { // if(image.length > 1) links.delete(image[1]) diff --git a/src/app/utils/RemarkableStripper.js b/src/app/utils/RemarkableStripper.js index f0c2a8287..9bbb8bfb8 100644 --- a/src/app/utils/RemarkableStripper.js +++ b/src/app/utils/RemarkableStripper.js @@ -1,23 +1,23 @@ -import Remarkable from 'remarkable' +import Remarkable from 'remarkable'; -const remarkable = new Remarkable() -export default remarkable +const remarkable = new Remarkable(); +export default remarkable; /** Removes all markdown leaving just plain text */ const remarkableStripper = md => { md.renderer.render = (tokens, options, env) => { - let str = '' + let str = ''; for (let i = 0; i < tokens.length; i++) { if (tokens[i].type === 'inline') { str += md.renderer.render(tokens[i].children, options, env); } else { // console.log('content', tokens[i]) - const content = tokens[i].content - str += (content || '') + ' ' + const content = tokens[i].content; + str += (content || '') + ' '; } } - return str - } -} + return str; + }; +}; -remarkable.use(remarkableStripper) // removes all markdown +remarkable.use(remarkableStripper); // removes all markdown diff --git a/src/app/utils/SanitizeConfig.js b/src/app/utils/SanitizeConfig.js index b4206a9dd..bf1e630fe 100644 --- a/src/app/utils/SanitizeConfig.js +++ b/src/app/utils/SanitizeConfig.js @@ -3,50 +3,70 @@ import { getPhishingWarningMessage } from 'shared/HtmlReady'; // the only allowa const iframeWhitelist = [ { re: /^(https?:)?\/\/player.vimeo.com\/video\/.*/i, - fn: (src) => { + fn: src => { // - if(!src) return null - const m = src.match(/https:\/\/player\.vimeo\.com\/video\/([0-9]+)/) - if(!m || m.length !== 2) return null - return 'https://player.vimeo.com/video/' + m[1] - } + if (!src) return null; + const m = src.match( + /https:\/\/player\.vimeo\.com\/video\/([0-9]+)/ + ); + if (!m || m.length !== 2) return null; + return 'https://player.vimeo.com/video/' + m[1]; + }, }, - { re: /^(https?:)?\/\/www.youtube.com\/embed\/.*/i, - fn: (src) => { - return src.replace(/\?.+$/, ''); // strip query string (yt: autoplay=1,controls=0,showinfo=0, etc) - } + { + re: /^(https?:)?\/\/www.youtube.com\/embed\/.*/i, + fn: src => { + return src.replace(/\?.+$/, ''); // strip query string (yt: autoplay=1,controls=0,showinfo=0, etc) + }, }, { re: /^https:\/\/w.soundcloud.com\/player\/.*/i, - fn: (src) => { - if(!src) return null + fn: src => { + if (!src) return null; // - const m = src.match(/url=(.+?)&/) - if(!m || m.length !== 2) return null - return 'https://w.soundcloud.com/player/?url=' + m[1] + + const m = src.match(/url=(.+?)&/); + if (!m || m.length !== 2) return null; + return ( + 'https://w.soundcloud.com/player/?url=' + + m[1] + '&auto_play=false&hide_related=false&show_comments=true' + '&show_user=true&show_reposts=false&visual=true' - } - } + ); + }, + }, ]; -export const noImageText = '(Image not shown due to low ratings)' +export const noImageText = '(Image not shown due to low ratings)'; export const allowedTags = ` div, iframe, del, a, p, b, i, q, br, ul, li, ol, img, h1, h2, h3, h4, h5, h6, hr, blockquote, pre, code, em, strong, center, table, thead, tbody, tr, th, td, strike, sup, sub -`.trim().split(/,\s*/) +` + .trim() + .split(/,\s*/); // Medium insert plugin uses: div, figure, figcaption, iframe -export default ({large = true, highQualityPost = true, noImage = false, sanitizeErrors = []}) => ({ +export default ({ + large = true, + highQualityPost = true, + noImage = false, + sanitizeErrors = [], +}) => ({ allowedTags, - // figure, figcaption, + // figure, figcaption, // SEE https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet allowedAttributes: { // "src" MUST pass a whitelist (below) - iframe: ['src', 'width', 'height', 'frameborder', 'allowfullscreen', - 'webkitallowfullscreen', 'mozallowfullscreen'], + iframe: [ + 'src', + 'width', + 'height', + 'frameborder', + 'allowfullscreen', + 'webkitallowfullscreen', + 'mozallowfullscreen', + ], // class attribute is strictly whitelisted (below) // and title is only set in the case of a phishing warning @@ -60,79 +80,103 @@ export default ({large = true, highQualityPost = true, noImage = false, sanitize transformTags: { iframe: (tagName, attribs) => { const srcAtty = attribs.src; - for(const item of iframeWhitelist) - if(item.re.test(srcAtty)) { - const src = typeof item.fn === 'function' ? item.fn(srcAtty, item.re) : srcAtty - if(!src) break + for (const item of iframeWhitelist) + if (item.re.test(srcAtty)) { + const src = + typeof item.fn === 'function' + ? item.fn(srcAtty, item.re) + : srcAtty; + if (!src) break; return { tagName: 'iframe', attribs: { frameborder: '0', allowfullscreen: 'allowfullscreen', webkitallowfullscreen: 'webkitallowfullscreen', // deprecated but required for vimeo : https://vimeo.com/forums/help/topic:278181 - mozallowfullscreen: 'mozallowfullscreen', // deprecated but required for vimeo + mozallowfullscreen: 'mozallowfullscreen', // deprecated but required for vimeo src, - width: large ? '640' : '480', + width: large ? '640' : '480', height: large ? '360' : '270', }, - } + }; } - console.log('Blocked, did not match iframe "src" white list urls:', tagName, attribs) - sanitizeErrors.push('Invalid iframe URL: ' + srcAtty) - return {tagName: 'div', text: `(Unsupported ${srcAtty})`} + console.log( + 'Blocked, did not match iframe "src" white list urls:', + tagName, + attribs + ); + sanitizeErrors.push('Invalid iframe URL: ' + srcAtty); + return { tagName: 'div', text: `(Unsupported ${srcAtty})` }; }, img: (tagName, attribs) => { - if(noImage) return {tagName: 'div', text: noImageText} + if (noImage) return { tagName: 'div', text: noImageText }; //See https://github.com/punkave/sanitize-html/issues/117 - let {src, alt} = attribs - if(!/^(https?:)?\/\//i.test(src)) { - console.log('Blocked, image tag src does not appear to be a url', tagName, attribs) - sanitizeErrors.push('An image in this post did not save properly.') - return {tagName: 'img', attribs: {src: 'brokenimg.jpg'}} + let { src, alt } = attribs; + if (!/^(https?:)?\/\//i.test(src)) { + console.log( + 'Blocked, image tag src does not appear to be a url', + tagName, + attribs + ); + sanitizeErrors.push( + 'An image in this post did not save properly.' + ); + return { tagName: 'img', attribs: { src: 'brokenimg.jpg' } }; } // replace http:// with // to force https when needed - src = src.replace(/^http:\/\//i, '//') - let atts = {src} - if(alt && alt !== '') atts.alt = alt - return {tagName, attribs: atts} + src = src.replace(/^http:\/\//i, '//'); + let atts = { src }; + if (alt && alt !== '') atts.alt = alt; + return { tagName, attribs: atts }; }, div: (tagName, attribs) => { - const attys = {} - const classWhitelist = ['pull-right', 'pull-left', 'text-justify', 'text-rtl', 'text-center', 'text-right', 'videoWrapper', 'phishy'] - const validClass = classWhitelist.find(e => attribs.class == e) - if(validClass) - attys.class = validClass - if (validClass === 'phishy' && attribs.title === getPhishingWarningMessage()) - attys.title = attribs.title + const attys = {}; + const classWhitelist = [ + 'pull-right', + 'pull-left', + 'text-justify', + 'text-rtl', + 'text-center', + 'text-right', + 'videoWrapper', + 'phishy', + ]; + const validClass = classWhitelist.find(e => attribs.class == e); + if (validClass) attys.class = validClass; + if ( + validClass === 'phishy' && + attribs.title === getPhishingWarningMessage() + ) + attys.title = attribs.title; return { tagName, - attribs: attys - } + attribs: attys, + }; }, td: (tagName, attribs) => { - const attys = {} - if(attribs.style === 'text-align:right') - attys.style = 'text-align:right' + const attys = {}; + if (attribs.style === 'text-align:right') + attys.style = 'text-align:right'; return { tagName, - attribs: attys - } + attribs: attys, + }; }, a: (tagName, attribs) => { - let {href} = attribs - if(!href) href = '#' - href = href.trim() - const attys = {href} + let { href } = attribs; + if (!href) href = '#'; + href = href.trim(); + const attys = { href }; // If it's not a (relative or absolute) steemit URL... if (!href.match(/^(\/(?!\/)|https:\/\/steemit.com)/)) { // attys.target = '_blank' // pending iframe impl https://mathiasbynens.github.io/rel-noopener/ - attys.rel = highQualityPost ? 'noopener' : 'nofollow noopener' + attys.rel = highQualityPost ? 'noopener' : 'nofollow noopener'; } return { tagName, - attribs: attys - } + attribs: attys, + }; }, - } -}) + }, +}); diff --git a/src/app/utils/ServerApiClient.js b/src/app/utils/ServerApiClient.js index eb757346e..a79585e61 100644 --- a/src/app/utils/ServerApiClient.js +++ b/src/app/utils/ServerApiClient.js @@ -1,5 +1,5 @@ -import {NTYPES, notificationsArrayToMap} from 'app/utils/Notifications'; -import {api} from '@steemit/steem-js'; +import { NTYPES, notificationsArrayToMap } from 'app/utils/Notifications'; +import { api } from '@steemit/steem-js'; const request_base = { method: 'post', @@ -7,91 +7,125 @@ const request_base = { credentials: 'same-origin', headers: { Accept: 'application/json', - 'Content-type': 'application/json' - } + 'Content-type': 'application/json', + }, }; export function serverApiLogin(account, signatures) { if (!process.env.BROWSER || window.$STM_ServerBusy) return; - const request = Object.assign({}, request_base, {body: JSON.stringify({account, signatures, csrf: $STM_csrf})}); + const request = Object.assign({}, request_base, { + body: JSON.stringify({ account, signatures, csrf: $STM_csrf }), + }); fetch('/api/v1/login_account', request); } export function serverApiLogout() { if (!process.env.BROWSER || window.$STM_ServerBusy) return; - const request = Object.assign({}, request_base, {body: JSON.stringify({csrf: $STM_csrf})}); + const request = Object.assign({}, request_base, { + body: JSON.stringify({ csrf: $STM_csrf }), + }); fetch('/api/v1/logout_account', request); } let last_call; export function serverApiRecordEvent(type, val, rate_limit_ms = 5000) { if (!process.env.BROWSER || window.$STM_ServerBusy) return; - if (last_call && (new Date() - last_call) < rate_limit_ms) return; + if (last_call && new Date() - last_call < rate_limit_ms) return; last_call = new Date(); const value = val && val.stack ? `${val.toString()} | ${val.stack}` : val; - const request = Object.assign({}, request_base, {body: JSON.stringify({csrf: $STM_csrf, type, value})}); - fetch('/api/v1/record_event', request); - api.call('overseer.collect', {collection: 'event', metadata: {type, value}}, (error) => { - // if (error) console.warn('overseer error', error, error.data); + const request = Object.assign({}, request_base, { + body: JSON.stringify({ csrf: $STM_csrf, type, value }), }); + fetch('/api/v1/record_event', request); + api.call( + 'overseer.collect', + { collection: 'event', metadata: { type, value } }, + error => { + // if (error) console.warn('overseer error', error, error.data); + } + ); } export function getNotifications(account) { - if (!process.env.BROWSER || window.$STM_ServerBusy) return Promise.resolve(null); - const request = Object.assign({}, request_base, {method: 'get'}); - return fetch(`/api/v1/notifications/${account}`, request).then(r => r.json()).then(res => { - return notificationsArrayToMap(res); -}); + if (!process.env.BROWSER || window.$STM_ServerBusy) + return Promise.resolve(null); + const request = Object.assign({}, request_base, { method: 'get' }); + return fetch(`/api/v1/notifications/${account}`, request) + .then(r => r.json()) + .then(res => { + return notificationsArrayToMap(res); + }); } export function markNotificationRead(account, fields) { - if (!process.env.BROWSER || window.$STM_ServerBusy) return Promise.resolve(null); - const request = Object.assign({}, request_base, {method: 'put', mode: 'cors'}); + if (!process.env.BROWSER || window.$STM_ServerBusy) + return Promise.resolve(null); + const request = Object.assign({}, request_base, { + method: 'put', + mode: 'cors', + }); const field_nums_str = fields.map(f => NTYPES.indexOf(f)).join('-'); - return fetch(`/api/v1/notifications/${account}/${field_nums_str}`, request).then(r => r.json()).then(res => { - return notificationsArrayToMap(res); -}); + return fetch(`/api/v1/notifications/${account}/${field_nums_str}`, request) + .then(r => r.json()) + .then(res => { + return notificationsArrayToMap(res); + }); } let last_page, last_views, last_page_promise; export function recordPageView(page, ref, account) { if (last_page_promise && page === last_page) return last_page_promise; - if (window.ga) { // virtual pageview + if (window.ga) { + // virtual pageview window.ga('set', 'page', page); window.ga('send', 'pageview'); } - api.call('overseer.pageview', {page, referer: ref, account}, (error) => { + api.call('overseer.pageview', { page, referer: ref, account }, error => { // if (error) console.warn('overseer error', error, error.data); }); - if (!process.env.BROWSER || window.$STM_ServerBusy) return Promise.resolve(0); - const request = Object.assign({}, request_base, {body: JSON.stringify({csrf: $STM_csrf, page, ref})}); - last_page_promise = fetch(`/api/v1/page_view`, request).then(r => r.json()).then(res => { - last_views = res.views; - return last_views; -}); + if (!process.env.BROWSER || window.$STM_ServerBusy) + return Promise.resolve(0); + const request = Object.assign({}, request_base, { + body: JSON.stringify({ csrf: $STM_csrf, page, ref }), + }); + last_page_promise = fetch(`/api/v1/page_view`, request) + .then(r => r.json()) + .then(res => { + last_views = res.views; + return last_views; + }); last_page = page; return last_page_promise; } export function webPushRegister(account, webpush_params) { if (!process.env.BROWSER || window.$STM_ServerBusy) return; - const request = Object.assign({}, request_base, {body: JSON.stringify({csrf: $STM_csrf, account, webpush_params})}); + const request = Object.assign({}, request_base, { + body: JSON.stringify({ csrf: $STM_csrf, account, webpush_params }), + }); fetch('/api/v1/notifications/register', request); } export function sendConfirmEmail(account) { - const request = Object.assign({}, request_base, {body: JSON.stringify({csrf: $STM_csrf, account})}); + const request = Object.assign({}, request_base, { + body: JSON.stringify({ csrf: $STM_csrf, account }), + }); fetch('/api/v1/notifications/send_confirm', request); } export function saveCords(x, y) { - const request = Object.assign({}, request_base, {body: JSON.stringify({csrf: $STM_csrf, x: x, y: y})}); + const request = Object.assign({}, request_base, { + body: JSON.stringify({ csrf: $STM_csrf, x: x, y: y }), + }); fetch('/api/v1/save_cords', request); } export function setUserPreferences(payload) { - if (!process.env.BROWSER || window.$STM_ServerBusy) return Promise.resolve(); - const request = Object.assign({}, request_base, {body: JSON.stringify({csrf: window.$STM_csrf, payload})}); + if (!process.env.BROWSER || window.$STM_ServerBusy) + return Promise.resolve(); + const request = Object.assign({}, request_base, { + body: JSON.stringify({ csrf: window.$STM_csrf, payload }), + }); return fetch('/api/v1/setUserPreferences', request); } diff --git a/src/app/utils/SlateEditor/Align.js b/src/app/utils/SlateEditor/Align.js index b1c7d154a..671866ad7 100644 --- a/src/app/utils/SlateEditor/Align.js +++ b/src/app/utils/SlateEditor/Align.js @@ -1,26 +1,30 @@ -import React from 'react' +import React from 'react'; export default class Align extends React.Component { - getAlignClass = () => { - const {node} = this.props - switch(node.data.get('align')) { - case 'text-right': return 'text-right'; - case 'text-left': return 'text-left'; - case 'text-center': return 'text-center'; - case 'pull-right': return 'pull-right'; - case 'pull-left': return 'pull-left'; + const { node } = this.props; + switch (node.data.get('align')) { + case 'text-right': + return 'text-right'; + case 'text-left': + return 'text-left'; + case 'text-center': + return 'text-center'; + case 'pull-right': + return 'pull-right'; + case 'pull-left': + return 'pull-left'; } - } + }; render = () => { - const { node, attributes, children } = this.props + const { node, attributes, children } = this.props; const className = this.getAlignClass(); return (
        {children}
        - ) - } + ); + }; } diff --git a/src/app/utils/SlateEditor/DemoState.js b/src/app/utils/SlateEditor/DemoState.js index 7dacf141a..e752e8e7a 100644 --- a/src/app/utils/SlateEditor/DemoState.js +++ b/src/app/utils/SlateEditor/DemoState.js @@ -1,14 +1,14 @@ export default { - "nodes": [ - { - "kind": "block", - "type": "paragraph", - "nodes": [ + nodes: [ { - "kind": "text", - "text": "" + kind: 'block', + type: 'paragraph', + nodes: [ + { + kind: 'text', + text: '', + }, + ], }, - ] - } - ] -} + ], +}; diff --git a/src/app/utils/SlateEditor/HRule.js b/src/app/utils/SlateEditor/HRule.js index 774a7ef0a..c5cf11dd8 100644 --- a/src/app/utils/SlateEditor/HRule.js +++ b/src/app/utils/SlateEditor/HRule.js @@ -1,10 +1,10 @@ -import React from 'react' +import React from 'react'; export default class HRule extends React.Component { render() { - const { node, state } = this.props - const isFocused = state.selection.hasEdgeIn(node) - const className = isFocused ? 'active' : null - return
        + const { node, state } = this.props; + const isFocused = state.selection.hasEdgeIn(node); + const className = isFocused ? 'active' : null; + return
        ; } } diff --git a/src/app/utils/SlateEditor/Helpers.js b/src/app/utils/SlateEditor/Helpers.js index 2e85d7293..d9b3d1239 100644 --- a/src/app/utils/SlateEditor/Helpers.js +++ b/src/app/utils/SlateEditor/Helpers.js @@ -2,17 +2,23 @@ const findParentTag = (el, tag, depth = 0) => { if (!el) return null; if (el.tagName == tag) return el; return findParentTag(el.parentNode, tag, depth + 1); -} +}; export const getCollapsedClientRect = () => { const selection = document.getSelection(); - if (selection.rangeCount === 0 || !selection.getRangeAt || !selection.getRangeAt(0) || !selection.getRangeAt(0).startContainer || !selection.getRangeAt(0).startContainer.getBoundingClientRect) { + if ( + selection.rangeCount === 0 || + !selection.getRangeAt || + !selection.getRangeAt(0) || + !selection.getRangeAt(0).startContainer || + !selection.getRangeAt(0).startContainer.getBoundingClientRect + ) { return null; } const node = selection.getRangeAt(0).startContainer; - if(! findParentTag(node, 'P')) return; // only show sidebar at the beginning of an empty

        + if (!findParentTag(node, 'P')) return; // only show sidebar at the beginning of an empty

        const rect = node.getBoundingClientRect(); return rect; -} +}; diff --git a/src/app/utils/SlateEditor/Iframe.js b/src/app/utils/SlateEditor/Iframe.js index f1dd15eae..822081d2c 100644 --- a/src/app/utils/SlateEditor/Iframe.js +++ b/src/app/utils/SlateEditor/Iframe.js @@ -1,60 +1,59 @@ -import React from 'react' -import linksRe from 'app/utils/Links' +import React from 'react'; +import linksRe from 'app/utils/Links'; export default class Iframe extends React.Component { - - normalizeEmbedUrl = (url) => { + normalizeEmbedUrl = url => { let match; // Detect youtube URLs - match = url.match(linksRe.youTubeId) - if(match && match.length >= 2) { - return 'https://www.youtube.com/embed/' + match[1] + match = url.match(linksRe.youTubeId); + if (match && match.length >= 2) { + return 'https://www.youtube.com/embed/' + match[1]; } // Detect vimeo - match = url.match(linksRe.vimeoId) - if(match && match.length >= 2) { - return 'https://player.vimeo.com/video/' + match[1] + match = url.match(linksRe.vimeoId); + if (match && match.length >= 2) { + return 'https://player.vimeo.com/video/' + match[1]; } - console.log("unable to auto-detect embed url", url) - return null - } + console.log('unable to auto-detect embed url', url); + return null; + }; - onChange = (e) => { - const { node, state, editor } = this.props - const value = e.target.value + onChange = e => { + const { node, state, editor } = this.props; + const value = e.target.value; - const src = this.normalizeEmbedUrl(value) || value + const src = this.normalizeEmbedUrl(value) || value; const next = editor .getState() .transform() - .setNodeByKey(node.key, {data: {src}}) - .apply() + .setNodeByKey(node.key, { data: { src } }) + .apply(); - editor.onChange(next) - } + editor.onChange(next); + }; - onClick = (e) => { + onClick = e => { // stop propagation so that the void node itself isn't focused, since that would unfocus the input. - e.stopPropagation() - } + e.stopPropagation(); + }; render = () => { - const { node, state, attributes } = this.props - const isFocused = state.selection.hasEdgeIn(node) - const className = isFocused ? 'active' : null + const { node, state, attributes } = this.props; + const isFocused = state.selection.hasEdgeIn(node); + const className = isFocused ? 'active' : null; const lockStyle = { position: 'absolute', - top: '0px', - left: '0px', - width: '100%', - height: '100%', + top: '0px', + left: '0px', + width: '100%', + height: '100%', background: 'rgba(0,0,0,0.1)', - } + }; return (

        @@ -65,50 +64,50 @@ export default class Iframe extends React.Component {
    - ) - } + ); + }; renderFrame = () => { - let src = this.props.node.data.get('src') - src = this.normalizeEmbedUrl(src) || src + let src = this.props.node.data.get('src'); + src = this.normalizeEmbedUrl(src) || src; return (