Skip to content

Commit

Permalink
first merge: very preleminary and needs testing
Browse files Browse the repository at this point in the history
  • Loading branch information
valzav committed Nov 6, 2016
1 parent 36e9488 commit 9af137b
Show file tree
Hide file tree
Showing 83 changed files with 2,494 additions and 795 deletions.
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"
}
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);
26 changes: 13 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 @@ -259,10 +259,10 @@ class CommentImpl extends React.Component {
controls = <div>
<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>}
<span className="Comment__footer__controls">
{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,7 +311,7 @@ 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" />
Expand All @@ -324,9 +324,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 +378,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
5 changes: 3 additions & 2 deletions app/components/cards/PostHistoryRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { Link } from 'react-router';
import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper';
import Icon from 'app/components/elements/Icon';
import { translate } from 'app/Translator';

export default class PostHistoryRow extends React.Component {

Expand All @@ -18,9 +19,9 @@ export default class PostHistoryRow extends React.Component {
let permlink = op[1].op[1].permlink;
let in_reply_to = <span></span>;
if( parent_author && parent_author != context )
in_reply_to = <span>in reply to <Link to={parent_link}>@{parent_author}</Link></span>;
in_reply_to = <span>{translate('in_reply_to') + ' '}<Link to={parent_link}>@{parent_author}</Link></span>;
else if( parent_author == context )
in_reply_to = <span><Link to={author_link}>@{author}</Link> replied to {parent_author}</span>;
in_reply_to = <span><Link to={author_link}>@{author}</Link> {' ' + translate('replied_to') + ' ' + parent_author}</span>;

// const content_markdown = op[1].op[1].body;
// const body = (<MarkdownViewer formId={} text={content_markdown} jsonMetadata={} />)
Expand Down
5 changes: 3 additions & 2 deletions app/components/cards/VoteHistoryRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { Link } from 'react-router';
import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper';
import Icon from 'app/components/elements/Icon';
import { translate } from 'app/Translator';

export default class VoteHistoryRow extends React.Component {

Expand All @@ -18,9 +19,9 @@ export default class VoteHistoryRow extends React.Component {
let permlink = op[1].op[1].permlink;
let in_reply_to = <span></span>;
if( parent_author && parent_author != context )
in_reply_to = <span>in reply to <Link to={parent_link}>@{parent_author}</Link></span>;
in_reply_to = <span>{translate('in_reply_to') + ' '} <Link to={parent_link}>@{parent_author}</Link></span>;
else if( parent_author == context )
in_reply_to = <span><Link to={author_link}>@{author}</Link> replied to {parent_author}</span>;
in_reply_to = <span><Link to={author_link}>@{author}</Link>{' ' + translate('replied_to')}{parent_author}</span>;

// const content_markdown = op[1].op[1].body;
// const body = (<MarkdownViewer formId={} text={content_markdown} jsonMetadata={} />)
Expand Down
31 changes: 31 additions & 0 deletions app/components/elements/ChangeAccountMeta.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';

// change it field by field
class ChangeAccountMeta extends Component {
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="metaKey">meta key</label>
<Field name="metaKey" component="input" type="text"/>
</div>
<div>
<label htmlFor="metaValue">meta value</label>
<Field name="metaValue" component="input" type="email"/>
</div>
<div>
<label htmlFor="passord">First Name</label>
<Field name="password" component="input" type="password"/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}

// Decorate the form component
ChangeAccountMeta = reduxForm({
form: 'changeAccountMeta' // a unique name for this form
})(ChangeAccountMeta);
Loading

0 comments on commit 9af137b

Please sign in to comment.