Skip to content

Commit

Permalink
Merge pull request steemit#612 from steemit/merge-master-develop
Browse files Browse the repository at this point in the history
release 0.1.161109
  • Loading branch information
roadscape authored Nov 9, 2016
2 parents e73d4e4 + d349ee7 commit a859e54
Show file tree
Hide file tree
Showing 85 changed files with 1,566 additions and 725 deletions.
2 changes: 1 addition & 1 deletion app/ResolveRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default function resolveRoute(path)
}
match = path.match(/^\/(@[\w\.\d-]+)\/?$/) ||
// @user/"posts" is deprecated in favor of "comments" as of oct-2016 (#443)
path.match(/^\/(@[\w\.\d-]+)\/(blog|posts|comments|recommended|transfers|curation-rewards|author-rewards|permissions|created|recent-replies|feed|password|followed|followers)\/?$/);
path.match(/^\/(@[\w\.\d-]+)\/(blog|posts|comments|recommended|transfers|curation-rewards|author-rewards|permissions|created|recent-replies|feed|password|followed|followers|settings)\/?$/);
if (match) {
return {page: 'UserProfile', params: match.slice(1)};
}
Expand Down
77 changes: 52 additions & 25 deletions app/Translator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import React from 'react';
import isString from 'lodash/isString';
import isObject from 'lodash/isObject';
import isUndefined from 'lodash/isUndefined';
import { connect } from 'react-redux'
import { IntlProvider, addLocaleData, injectIntl } from 'react-intl';
import store from 'store';
import { DEFAULT_LANGUAGE } from 'config/client_config';

// most of this code creates a wrapper for i18n API.
// this is needed to make i18n future proof

/*
module exports two functions: translate and translateHtml
usage example:
translate('reply_to_user', {username: 'undeadlol1') == 'Reply to undeadlol1'
translateHtml works the same, expcept it renders string with html tags in it
module exports two functions: translate and translateHtml
usage example:
translate('reply_to_user', {username: 'undeadlol1') == 'Reply to undeadlol1'
translateHtml works the same, expcept it renders string with html tags in it
*/

// locale data is needed for various messages, ie 'N minutes ago'
Expand All @@ -28,7 +31,7 @@ import { ru } from './locales/ru';
import { fr } from './locales/fr';
import { es } from './locales/es';
import { it } from './locales/it';
const translations = {
const messages = {
en: en,
ru: ru,
fr: fr,
Expand All @@ -38,9 +41,18 @@ const translations = {

// exported function placeholders
// this is needed for proper export before react-intl functions with locale data,
// will be properly created (they depend on react props and context,
// which is not available until component is being created)
let translate = () => {};
// will be properly created (they depend on react props and context),
// which is not available until component is being created
//
/*
this placeholder is needed for usage outside of react. In server side code and in static html files.
This function is very simple, it does NOT support dynamic values (for example: translate('your_email_is', {email: '[email protected]'})). Use it carefully
*/
let translate = string => {
let language = DEFAULT_LANGUAGE
if (process.env.BROWSER) language = store.get('language') || DEFAULT_LANGUAGE
return messages[language][string]
};
let translateHtml = () => {};
let translatePlural = () => {};

Expand Down Expand Up @@ -105,24 +117,30 @@ class Translator extends React.Component {
// Define user's language. Different browsers have the user locale defined
// on different fields on the `navigator` object, so we make sure to account
// for these different by checking all of them
let language = 'en';
// while Server Side Rendering is in process, 'navigator' is undefined
let language = this.props.locale; // usually 'en'
if (process.env.BROWSER) {
language = navigator ? (navigator.languages && navigator.languages[0])
|| navigator.language
|| navigator.userLanguage
: 'en';
const storredLanguage = store.get('language')
if (storredLanguage) language = storredLanguage
}

//Split locales with a region code (ie. 'en-EN' to 'en')
const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0];

// TODO: don't forget to add Safari polyfill

// to ensure dynamic language change, "key" property with same "locale" info must be added
// see: https://github.com/yahoo/react-intl/wiki/Components#multiple-intl-contexts
let messages = translations[languageWithoutRegionCode]
return <IntlProvider locale={languageWithoutRegionCode} key={languageWithoutRegionCode} messages={messages}>
// let language = DEFAULT_LANGUAGE; // usually 'en'
// while Server Side Rendering is in process, 'navigator' is undefined
// currently commented out, because in golos we need only russian
// if (process.env.BROWSER) language = navigator
// ? (navigator.languages && navigator.languages[0])
// || navigator.language
// || navigator.userLanguage
// : DEFAULT_LANGUAGE;
//Split locales with a region code (ie. 'en-EN' to 'en')
const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0];

return <IntlProvider
// to ensure dynamic language change, "key" property with same "locale" info must be added
// see: https://github.com/yahoo/react-intl/wiki/Components#multiple-intl-contexts
key={languageWithoutRegionCode}
defaultLocale={DEFAULT_LANGUAGE}
locale={languageWithoutRegionCode}
messages={messages[languageWithoutRegionCode]}
>
<div>
<DummyComponentToExportProps />
{this.props.children}
Expand All @@ -133,4 +151,13 @@ class Translator extends React.Component {

export { translate, translateHtml, translatePlural }

export default Translator
export default connect(
// mapStateToProps
(state, ownProps) => {
const locale = state.user.get('locale')
return {
...ownProps,
locale
}
}
)(Translator)
9 changes: 9 additions & 0 deletions app/assets/images/favicons/browserconfig.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/images/favicons/mstile-150x150.png"/>
<TileColor>#ffffff</TileColor>
</tile>
</msapplication>
</browserconfig>
17 changes: 17 additions & 0 deletions app/assets/images/favicons/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +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"
}
8 changes: 4 additions & 4 deletions app/components/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class App extends React.Component {
</li>
<li>
<a href="https://steem.io/SteemWhitePaper.pdf" onClick={this.navigate}>
{translate("whitepaper")}
{translate("APP_NAME_whitepaper")}
</a>
</li>
<li>
Expand All @@ -177,12 +177,12 @@ class App extends React.Component {
</li>
<li>
<a onClick={() => depositSteem()}>
{translate("buy_steem")}
{translate("buy_LIQUID_TOKEN")}
</a>
</li>
<li>
<a href="http://steemtools.com/" onClick={this.navigate}>
{translate('steem_app_center')}
{translate('APP_NAME_app_center')}
</a>
</li>
<li>
Expand All @@ -202,7 +202,7 @@ class App extends React.Component {
</li>
<li>
<a href="https://steemit.chat/home" target="_blank" rel="noopener noreferrer">
{translate("steemit_chat")}&nbsp;<Icon name="extlink" />
{translate("APP_NAME_chat")}&nbsp;<Icon name="extlink" />
</a>
</li>
<li>
Expand Down
5 changes: 3 additions & 2 deletions app/components/cards/CardView.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {connect} from 'react-redux'
import Link from 'app/components/elements/Link'
import g from 'app/redux/GlobalReducer'
import links from 'app/utils/Links'
import { translate } from 'app/Translator';

/** @deprecated */
class CardView extends React.Component {
Expand Down Expand Up @@ -43,13 +44,13 @@ class CardView extends React.Component {
const youTubeImage = links.youTube.test(link)
return <span className="Card">
{image && !youTubeImage && <div>
{canEdit && <div>(<a onClick={this.onCloseImage}>remove</a>)<br /></div>}
{canEdit && <div>(<a onClick={this.onCloseImage}>{translate('remove')}</a>)<br /></div>}
<Link href={link}>
<img src={image} alt={alt} />
</Link>
</div>}
{description && <div>
{canEdit && <span>(<a onClick={this.onCloseDescription}>remove</a>)</span>}
{canEdit && <span>(<a onClick={this.onCloseDescription}>{translate('remove')}</a>)</span>}
<Link href={link}>
<blockquote>{description}</blockquote>
</Link>
Expand Down
30 changes: 15 additions & 15 deletions app/components/cards/CategorySelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import {connect} from 'react-redux'
import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'
import {cleanReduxInput} from 'app/utils/ReduxForms'
import { translate } from '../../Translator.js';

class CategorySelector extends React.Component {
static propTypes = {
Expand All @@ -21,7 +22,6 @@ class CategorySelector extends React.Component {
}
static defaultProps = {
autoComplete: 'on',
placeholder: 'Tag (up to 5 tags), the first tag is your main category.',
id: 'CategorySelectorId',
isEdit: false,
}
Expand Down Expand Up @@ -60,7 +60,7 @@ class CategorySelector extends React.Component {

const categorySelect = (
<select {...cleanReduxInput(this.props)} onChange={this.categorySelectOnChange} ref="categoryRef" tabIndex={tabIndex} disabled={disabled}>
<option value="">Select a tag...</option>
<option value="">{translate('select_a_tag')}...</option>
{categoryOptions}
<option value="new">{this.props.placeholder}</option>
</select>
Expand All @@ -73,25 +73,25 @@ class CategorySelector extends React.Component {
}
}
export function validateCategory(category, required = true) {
if(!category || category.trim() === '') return required ? 'Required' : null
if(!category || category.trim() === '') return required ? translate( 'required' ) : null
const cats = category.trim().split(' ')
return (
// !category || category.trim() === '' ? 'Required' :
cats.length > 5 ? 'Please use only five categories' :
cats.find(c => c.length > 24) ? 'Maximum tag length is 24 characters' :
cats.find(c => c.split('-').length > 2) ? 'Use only one dash' :
cats.find(c => c.indexOf(',') >= 0) ? 'Use spaces to separate tags' :
cats.find(c => /[A-Z]/.test(c)) ? 'Use only lowercase letters' :
cats.find(c => !/^[a-z0-9-#]+$/.test(c)) ? 'Use only lowercase letters, digits and one dash' :
cats.find(c => !/^[a-z-#]/.test(c)) ? 'Must start with a letter' :
cats.find(c => !/[a-z0-9]$/.test(c)) ? 'Must end with a letter or number' :
cats.length > 5 ? translate('use_limitied_amount_of_categories', {amount: 5}) :
cats.find(c => c.length > 24) ? translate('maximum_tag_length_is_24_characters') :
cats.find(c => c.split('-').length > 2) ? translate('use_one_dash') :
cats.find(c => c.indexOf(',') >= 0) ? translate('use_spaces_to_separate_tags') :
cats.find(c => /[A-Z]/.test(c)) ? translate('use_only_lowercase_letters') :
cats.find(c => !/^[a-z0-9-#]+$/.test(c)) ? translate('use_only_allowed_characters') :
cats.find(c => !/^[a-z-#]/.test(c)) ? translate('must_start_with_a_letter') :
cats.find(c => !/[a-z0-9]$/.test(c)) ? translate('must_end_with_a_letter_or_number') :
null
)
}
export default connect((state, ownProps) => {
const trending = state.global.get('category_idx').get('trending')
return {
trending,
...ownProps,
}
// apply translations
// they are used here because default prop can't acces intl property
const placeholder = translate('tag_your_story');
return { trending, placeholder, ...ownProps, }
})(CategorySelector);
28 changes: 15 additions & 13 deletions app/components/cards/Comment.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import Icon from 'app/components/elements/Icon';
import Userpic from 'app/components/elements/Userpic';
import transaction from 'app/redux/Transaction'
import {List} from 'immutable'
import pluralize from 'pluralize';
import { translate } from 'app/Translator';

export function sortComments( g, comments, sort_order ){
export function sortComments( g, comments, sort_order ) {

function netNegative(a) {
return a.get("net_rshares") < 0;
Expand Down Expand Up @@ -85,7 +85,7 @@ class CommentImpl extends React.Component {
// html props
global: React.PropTypes.object.isRequired,
content: React.PropTypes.string.isRequired,
sort_order: React.PropTypes.oneOf(['active', 'update', 'created', 'trending']).isRequired,
sort_order: React.PropTypes.oneOf(['active', 'updated', 'created', 'trending']).isRequired,
root: React.PropTypes.bool,
showNegativeComments: React.PropTypes.bool,
onHide: React.PropTypes.func,
Expand Down Expand Up @@ -166,7 +166,7 @@ class CommentImpl extends React.Component {

/**
* - `hide` is based on author reputation, and will hide the entire post on initial render.
* - `hide_body` is true when comment rshares OR author rep is negative.
* - `hide_body` is true when comment rshares OR author rep is negative.
* it hides the comment body (but not the header) until the "reveal comment" link is clicked.
*/
_checkHide(props) {
Expand Down Expand Up @@ -216,7 +216,7 @@ class CommentImpl extends React.Component {
let g = this.props.global;
const dis = g.get('content').get(this.props.content);
if (!dis) {
return <div>Loading...</div>
return <div>{translate('loading')}...</div>
}
const comment = dis.toJS();
if(!comment.stats) {
Expand Down Expand Up @@ -260,9 +260,9 @@ class CommentImpl extends React.Component {
<Voting post={post} />
{!readonly &&
<span className="Comment__footer__controls">
{depth < 6 && <a onClick={onShowReply}>Reply</a>}
{' '}{showEditOption && <a onClick={onShowEdit}>Edit</a>}
{' '}{showDeleteOption && <a onClick={onDeletePost}>Delete</a>}
{depth < 6 && <a onClick={onShowReply}>{translate('reply')}</a>}
{' '}{showEditOption && <a onClick={onShowEdit}>{translate('edit')}</a>}
{' '}{showDeleteOption && <a onClick={onDeletePost}>{translate('delete')}</a>}
</span>}
</div>;
}
Expand Down Expand Up @@ -311,10 +311,12 @@ class CommentImpl extends React.Component {
<div className="Comment__header">
<div className="Comment__header_collapse">
<Voting post={post} flag />
<a title="Collapse/Expand" onClick={this.toggleCollapsed}>{ this.state.collapsed ? '[+]' : '[-]' }</a>
<a title={translate('collapse_or_expand')} onClick={this.toggleCollapsed}>{ this.state.collapsed ? '[+]' : '[-]' }</a>
</div>
<span className="Comment__header-user">
<Icon name="user" className="Comment__Userpic-small" />
<div className="Comment__Userpic-small">
<Userpic account={comment.author} />
</div>
<Author author={comment.author} authorRepLog10={authorRepLog10} />
</span>
&nbsp; &middot; &nbsp;
Expand All @@ -324,9 +326,9 @@ class CommentImpl extends React.Component {
{ (this.state.collapsed || hide_body) &&
<Voting post={post} showList={false} /> }
{ this.state.collapsed && comment.children > 0 &&
<span className="marginLeft1rem">{pluralize('replies', comment.children, true)}</span>}
<span className="marginLeft1rem">{translate('reply_count', {replyCount: comment.children})}</span>}
{ !this.state.collapsed && hide_body &&
<a className="marginLeft1rem" onClick={this.revealBody}>reveal comment</a>}
<a className="marginLeft1rem" onClick={this.revealBody}>{translate('reveal_comment')}</a>}
</div>
<div className="Comment__body entry-content">
{showEdit ? renderedEditor : body}
Expand Down Expand Up @@ -378,7 +380,7 @@ const Comment = connect(
dispatch(transaction.actions.broadcastOperation({
type: 'delete_comment',
operation: {author, permlink},
confirm: 'Are you sure?'
confirm: translate('are_you_sure'),
}))
},
})
Expand Down
14 changes: 10 additions & 4 deletions app/components/cards/Comment.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@
}

.Comment__Userpic {
float: left;
position: relative;
top: 0.4rem;
padding-right: 0.6rem;
}

.Comment__Userpic-small {
position: relative;
left: -2px;
@include hide-for(medium);
path {
fill: $dark_gray;
.Userpic {
float: left;
position: relative;
padding-right: 0.2rem;
img {
width: 16px;
height: 16px;
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion app/components/cards/PostFull.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ export default class PostFull extends React.Component {
}

const readonly = post_content.get('mode') === 'archived' || $STM_Config.read_only_mode
const showPromote = username && post_content.get('mode') === "first_payout" && post_content.get('depth') == 0
//const showPromote = username && post_content.get('mode') === "first_payout" && post_content.get('depth') == 0
const showPromote = false // TODO: revert when nodes are updated with https://github.com/steemit/steem/pull/550
const showReplyOption = post_content.get('depth') < 6
const showEditOption = username === author
const authorRepLog10 = repLog10(content.author_reputation)
Expand Down
Loading

0 comments on commit a859e54

Please sign in to comment.