Developers: add challenge to become a developer
This commit is contained in:
parent
c80d00e67f
commit
3af8313b42
7 changed files with 182 additions and 52 deletions
84
app/soapbox/features/developers/developers_challenge.js
Normal file
84
app/soapbox/features/developers/developers_challenge.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import Column from '../ui/components/column';
|
||||
import { SimpleForm, TextInput } from 'soapbox/features/forms';
|
||||
import { changeSetting } from 'soapbox/actions/settings';
|
||||
import snackbar from 'soapbox/actions/snackbar';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.developers', defaultMessage: 'Developers' },
|
||||
answerLabel: { id: 'developers.challenge.answer_label', defaultMessage: 'Answer' },
|
||||
answerPlaceholder: { id: 'developers.challenge.answer_placeholder', defaultMessage: 'Your answer' },
|
||||
success: { id: 'developers.challenge.success', defaultMessage: 'You are now a developer' },
|
||||
fail: { id: 'developers.challenge.fail', defaultMessage: 'Wrong answer' },
|
||||
});
|
||||
|
||||
export default @connect()
|
||||
@injectIntl
|
||||
class DevelopersChallenge extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
answer: '',
|
||||
}
|
||||
|
||||
handleChangeAnswer = e => {
|
||||
this.setState({ answer: e.target.value });
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
const { intl, dispatch } = this.props;
|
||||
const { answer } = this.state;
|
||||
|
||||
if (answer === 'buzzfizz') {
|
||||
dispatch(changeSetting(['isDeveloper'], true));
|
||||
dispatch(snackbar.success(intl.formatMessage(messages.success)));
|
||||
} else {
|
||||
dispatch(snackbar.error(intl.formatMessage(messages.fail)));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props;
|
||||
|
||||
const challenge = `function fizzbuzz() {
|
||||
return 'fizz|buzz'.split('|').reverse().join('');
|
||||
}`;
|
||||
|
||||
return (
|
||||
<Column heading={intl.formatMessage(messages.heading)}>
|
||||
<div className='developers-challenge'>
|
||||
<SimpleForm onSubmit={this.handleSubmit}>
|
||||
<FormattedMessage
|
||||
id='developers.challenge.message'
|
||||
defaultMessage='What is the result of calling {function}?'
|
||||
values={{ function: <span className='code'>fizzbuzz()</span> }}
|
||||
/>
|
||||
<pre className='code'>
|
||||
{challenge}
|
||||
</pre>
|
||||
<TextInput
|
||||
name='answer'
|
||||
label={intl.formatMessage(messages.answerLabel)}
|
||||
placeholder={intl.formatMessage(messages.answerPlaceholder)}
|
||||
onChange={this.handleChangeAnswer}
|
||||
value={this.state.answer}
|
||||
/>
|
||||
<div className='actions'>
|
||||
<button name='button' type='submit' className='btn button button-primary'>
|
||||
<FormattedMessage id='developers.challenge.submit' defaultMessage='Become a developer' />
|
||||
</button>
|
||||
</div>
|
||||
</SimpleForm>
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
60
app/soapbox/features/developers/developers_menu.js
Normal file
60
app/soapbox/features/developers/developers_menu.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Column from '../ui/components/column';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.developers', defaultMessage: 'Developers' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
class DevelopersMenu extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<Column heading={intl.formatMessage(messages.heading)}>
|
||||
<div className='dashcounters'>
|
||||
<div className='dashcounter'>
|
||||
<Link to='/developers/apps/create'>
|
||||
<div className='dashcounter__icon'>
|
||||
<Icon src={require('@tabler/icons/icons/apps.svg')} />
|
||||
</div>
|
||||
<div className='dashcounter__label'>
|
||||
<FormattedMessage id='developers.navigation.app_create_label' defaultMessage='Create an app' />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='dashcounter'>
|
||||
<Link to='/developers/settings_store'>
|
||||
<div className='dashcounter__icon'>
|
||||
<Icon src={require('@tabler/icons/icons/code-plus.svg')} />
|
||||
</div>
|
||||
<div className='dashcounter__label'>
|
||||
<FormattedMessage id='developers.navigation.settings_store_label' defaultMessage='Settings store' />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='dashcounter'>
|
||||
<Link to='/error'>
|
||||
<div className='dashcounter__icon'>
|
||||
<Icon src={require('@tabler/icons/icons/mood-sad.svg')} />
|
||||
</div>
|
||||
<div className='dashcounter__label'>
|
||||
<FormattedMessage id='developers.navigation.intentional_error_label' defaultMessage='Trigger an error' />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,60 +1,28 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Column from '../ui/components/column';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import { connect } from 'react-redux';
|
||||
import { getSettings } from 'soapbox/actions/settings';
|
||||
import DevelopersMenu from './developers_menu';
|
||||
import DevelopersChallenge from './developers_challenge';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.developers', defaultMessage: 'Developers' },
|
||||
});
|
||||
const mapStateToProps = state => {
|
||||
const settings = getSettings(state);
|
||||
|
||||
export default @injectIntl
|
||||
return {
|
||||
isDeveloper: settings.get('isDeveloper'),
|
||||
};
|
||||
};
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
class Developers extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
isDeveloper: PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<Column heading={intl.formatMessage(messages.heading)}>
|
||||
<div className='dashcounters'>
|
||||
<div className='dashcounter'>
|
||||
<Link to='/developers/apps/create'>
|
||||
<div className='dashcounter__icon'>
|
||||
<Icon src={require('@tabler/icons/icons/apps.svg')} />
|
||||
</div>
|
||||
<div className='dashcounter__label'>
|
||||
<FormattedMessage id='developers.navigation.app_create_label' defaultMessage='Create an app' />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='dashcounter'>
|
||||
<Link to='/developers/settings_store'>
|
||||
<div className='dashcounter__icon'>
|
||||
<Icon src={require('@tabler/icons/icons/code-plus.svg')} />
|
||||
</div>
|
||||
<div className='dashcounter__label'>
|
||||
<FormattedMessage id='developers.navigation.settings_store_label' defaultMessage='Settings store' />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='dashcounter'>
|
||||
<Link to='/error'>
|
||||
<div className='dashcounter__icon'>
|
||||
<Icon src={require('@tabler/icons/icons/mood-sad.svg')} />
|
||||
</div>
|
||||
<div className='dashcounter__label'>
|
||||
<FormattedMessage id='developers.navigation.intentional_error_label' defaultMessage='Trigger an error' />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
const { isDeveloper } = this.props;
|
||||
return isDeveloper ? <DevelopersMenu /> : <DevelopersChallenge />;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -282,10 +282,6 @@ class Preferences extends ImmutablePureComponent {
|
|||
hint={<FormattedMessage id='preferences.hints.demetricator' defaultMessage='Decrease social media anxiety by hiding all numbers from the site.' />}
|
||||
path={['demetricator']}
|
||||
/>
|
||||
<SettingsCheckbox
|
||||
label={<FormattedMessage id='preferences.fields.developer_label' defaultMessage='Developer tools' />}
|
||||
path={['isDeveloper']}
|
||||
/>
|
||||
</FieldsGroup>
|
||||
</SimpleForm>
|
||||
</Column>
|
||||
|
|
|
@ -324,7 +324,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
|||
|
||||
<WrappedRoute path='/developers/apps/create' developerOnly page={DefaultPage} component={CreateApp} content={children} />
|
||||
<WrappedRoute path='/developers/settings_store' developerOnly page={DefaultPage} component={SettingsStore} content={children} />
|
||||
<WrappedRoute path='/developers' developerOnly page={DefaultPage} component={Developers} content={children} />
|
||||
<WrappedRoute path='/developers' page={DefaultPage} component={Developers} content={children} />
|
||||
<WrappedRoute path='/error' page={EmptyPage} component={IntentionalError} content={children} />
|
||||
|
||||
<WrappedRoute path='/donate/crypto' publicRoute page={DefaultPage} component={CryptoDonate} content={children} />
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
@import 'navigation';
|
||||
@import 'placeholder';
|
||||
@import 'autosuggest';
|
||||
@import 'developers';
|
||||
|
||||
// COMPONENTS
|
||||
@import 'components/buttons';
|
||||
|
|
21
app/styles/developers.scss
Normal file
21
app/styles/developers.scss
Normal file
|
@ -0,0 +1,21 @@
|
|||
.developers-challenge {
|
||||
.code {
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
cursor: text;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
span.code {
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre.code {
|
||||
line-height: 1.6em;
|
||||
overflow-x: auto;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
margin: 20px 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue