Unverified Commit 09e5463e authored by roadscape's avatar roadscape Committed by GitHub
Browse files

Merge pull request #107 from steemit/106-community-create-UI-tweaks

106 community create ui tweaks
parents ffd08186 aace1cf4
......@@ -25,6 +25,7 @@
@import './elements/SanitizedLink/styles';
@import './elements/HelpTip/styles';
@import './elements/Dropdown';
@import './elements/CreateCommunity';
// modules
@import './modules/Header/styles';
......
......@@ -4,6 +4,8 @@ import { connect } from 'react-redux';
import * as communityActions from 'app/redux/CommunityReducer';
import tt from 'counterpart';
import { key_utils } from '@steemit/steem-js/lib/auth/ecc';
import LoadingIndicator from 'app/components/elements/LoadingIndicator';
import Unicode from 'app/utils/Unicode';
class CreateCommunity extends React.Component {
constructor() {
......@@ -23,19 +25,24 @@ class CreateCommunity extends React.Component {
communityCreateSuccess,
createCommunity,
communityDescription,
communityNSFW,
communityOwnerWifPassword,
communityOwnerName,
communityTitle,
updateCommunityTitle,
updateCommunityDescription,
updateCommunityNSFW,
updateCommunityOwnerAccountName,
updateCommunityOwnerWifPassword,
broadcastOps,
communityCreationPending,
socialUrl,
} = this.props;
const handleAccountCreateError = () => {
const handleAccountCreateError = error => {
// If the user cancels the account creation do not show an error.
if (error === undefined) {
communityCreationPending(false);
return;
}
this.setState({ accountError: true });
};
......@@ -53,15 +60,13 @@ class CreateCommunity extends React.Component {
}
updateCommunityTitle(e.target.value);
};
const handleCommunityDescriptionInput = e => {
if (e.target.value.length > 120) {
return;
}
updateCommunityDescription(e.target.value);
};
const handleCommunityNSFWInput = e => {
updateCommunityNSFW(e.target.checked);
};
const handleCommunitySubmit = e => {
e.preventDefault();
......@@ -69,7 +74,6 @@ class CreateCommunity extends React.Component {
accountName,
communityTitle,
communityDescription,
communityNSFW,
communityOwnerName,
communityOwnerWifPassword,
createAccountSuccessCB: handleAccountCreateSuccess,
......@@ -101,160 +105,115 @@ class CreateCommunity extends React.Component {
updateCommunityOwnerAccountName(ownerUsername);
};
const generateCommunityCredentials = () => {
const generateCreds = () => {
generateWif();
generateUsername();
};
const generateCommunityCredentialsButton = (
<button
type="button"
className="button hollow"
onClick={generateCommunityCredentials}
>
{tt('g.click_to_generate_password')}
<button type="button" className="button" onClick={generateCreds}>
Next
</button>
);
const rememberCredentialsPrompt = (
<div>
<div>{`${tt(
'g.community_owner_name_is'
)}: ${communityOwnerName}`}</div>
<div>{`${tt(
'g.community_password_is'
)}: ${communityOwnerWifPassword}`}</div>
</div>
);
const rememberCredentialsCheckbox1 = (
<label htmlFor="box1">
<input type="checkbox" name="box1" required />
{tt('g.understand_that_APP_NAME_cannot_recover_password', {
APP_NAME,
})}.
</label>
);
const rememberCredentialsCheckbox2 = (
<label htmlFor="box2">
<input type="checkbox" name="box2" required />
{tt('g.i_saved_password')}.
</label>
);
const submitCreateCommunityFormButton = (
<input type="submit" value="Submit" />
);
const createCommunityAccountSuccessMessage = (
<div>
Community account created on the blockchain. Setting current
user to be community admin...
</div>
);
const createCommunityAccountErrorMessage = (
const credentialsPane = (
<div>
Unable to create that community. Please ensure you used the
correct key.
<label>
Owner Name / Password
<code className="pwd">
{communityOwnerName}
<br />
{communityOwnerWifPassword}
</code>
</label>
<label style={{ marginTop: '0px' }}>
<input type="checkbox" name="box2" required />
I have securely saved my owner name and password.
</label>
</div>
);
const createCommunityBroadcastOpsErrorMessage = (
<div>
The community was created but setting current user to be admin
failed. Wait a moment and try again
</div>
const submitCreateCommunityFormButton = error => (
<input
className="button"
type="submit"
value="Create Community"
disabled={!!error}
/>
);
const createCommunitySuccessMessage = (
<div>
<p>Your community was created!</p>
<a
href={`https://steemitdev.com/trending/${communityOwnerName}`}
>
{tt('g.community_visit')}
</a>
</div>
);
const createCommunityErrorMessage = (
<div>{tt('g.community_error')}</div>
);
const hasPass = communityOwnerWifPassword.length > 0;
const createCommunityLoading = <div>{tt('g.community_creating')}</div>;
let formError = null;
const rx = new RegExp('^[' + Unicode.L + ']');
if (!rx.test(communityTitle) && (communityTitle || hasPass))
formError = 'Must start with a letter.';
const createCommunityForm = (
<form onSubmit={handleCommunitySubmit}>
const form = (
<form className="community--form" onSubmit={handleCommunitySubmit}>
<div>{tt('g.community_create')}</div>
<label htmlFor="community_title">
<label>
Title
<input
id="community_title"
name="community_title"
type="text"
minLength="4"
maxLength="30"
minLength="3"
maxLength="20"
onChange={handleCommunityTitleInput}
value={communityTitle}
required
/>
</label>
<label htmlFor="community_description">
{formError && <span className="error">{formError}</span>}
<label>
{tt('g.community_description')}
<input
id="community_description"
name="community_description"
type="text"
minLength="10"
maxLength="140"
maxLength="120"
onChange={handleCommunityDescriptionInput}
value={communityDescription}
required
/>
</label>
<label id="is_nsfw" htmlFor="is_nsfw">
{tt('g.community_nsfw')}
<input
type="checkbox"
name="is_nsfw"
checked={communityNSFW}
onChange={handleCommunityNSFWInput}
/>
</label>
{communityOwnerWifPassword.length <= 0 &&
generateCommunityCredentialsButton}
{communityOwnerWifPassword.length > 0 &&
rememberCredentialsPrompt}
{communityOwnerWifPassword.length > 0 &&
rememberCredentialsCheckbox1}
{communityOwnerWifPassword.length > 0 &&
rememberCredentialsCheckbox2}
{communityOwnerWifPassword.length > 0 &&
submitCreateCommunityFormButton}
{!hasPass && generateCommunityCredentialsButton}
{hasPass && credentialsPane}
{hasPass && submitCreateCommunityFormButton(formError)}
</form>
);
const accountCreated = this.state.accountCreated;
const accountError = this.state.accountError;
const settingsError = this.state.broadcastOpsError;
const errored = accountError || settingsError;
const pending = communityCreatePending && !errored;
const finished = communityCreateSuccess;
const sagaError = communityCreateError;
if (finished) {
const url = `${socialUrl}/trending/${communityOwnerName}`;
return (
<div className="row">
<div className="column large-6 small-12">
Your community was created!<br />
<strong>
<a href={url}>Get started.</a>
</strong>
</div>
</div>
);
}
const showErr = msg => <div className="community--error">{msg}</div>;
const adminMsg = `Account created. Setting @${accountName} as admin...`;
return (
<div className="row">
<div className="column large-6 small-12">
{this.state.accountError &&
createCommunityAccountErrorMessage}
{this.state.accountCreated &&
!communityCreateSuccess &&
createCommunityAccountSuccessMessage}
{this.state.broadcastOpsError &&
createCommunityBroadcastOpsErrorMessage}
{this.state.accountError && createCommunityForm}
{this.state.broadcastOpsError && createCommunityForm}
{!communityCreatePending &&
!communityCreateSuccess &&
createCommunityForm}
{communityCreatePending &&
!this.state.accountError &&
!this.state.broadcastOpsError &&
createCommunityLoading}
{communityCreateSuccess && createCommunitySuccessMessage}
{communityCreateError && createCommunityErrorMessage}
{accountError && showErr('Account creation failed.')}
{settingsError && showErr('Update settings failed.')}
{sagaError && showErr('Failed. Please report this issue.')}
{accountCreated && <div>{adminMsg}</div>}
{pending ? <LoadingIndicator type="circle" /> : form}
</div>
</div>
);
......@@ -269,11 +228,13 @@ export default connect(
const current = state.user.get('current');
const username = current && current.get('username');
const isMyAccount = username === accountName;
const socialUrl = state.app.get('socialUrl');
return {
...ownProps,
...state.community.toJS(),
isMyAccount,
accountName,
socialUrl,
};
},
// mapDispatchToProps
......@@ -285,9 +246,6 @@ export default connect(
updateCommunityDescription: description => {
dispatch(communityActions.setCommunityDescription(description));
},
updateCommunityNSFW: isNSFW => {
dispatch(communityActions.setCommunityNSFW(isNSFW));
},
updateCommunityOwnerAccountName: accountName => {
dispatch(
communityActions.setCommunityOwnerAccountName(accountName)
......@@ -318,6 +276,13 @@ export default connect(
)
);
},
communityCreationPending: createCommunityAccountPending => {
dispatch(
communityActions.createCommunityAccountPending(
createCommunityAccountPending
)
);
},
};
}
)(CreateCommunity);
.community--form {
label {margin-top: 1rem;}
.button {margin-top: 1rem;}
.pwd {
text-transform: none;
display: block;
padding: 0.2rem 0.5rem;
color: rgb(199, 37, 78);
font-size: 1rem;
background: #fff;
word-break: break-all;
text-align: left;
}
}
.community--error {
color: rgb(199, 37, 78);
}
......@@ -29,8 +29,7 @@
"community_error":
"Oops, there was an error creating the community, please try again.",
"community_create": "CREATE A COMMUNITY",
"community_description": "Description",
"community_nsfw": "Is Not Safe For Work",
"community_description": "About",
"confirm": "Confirm",
"convert": "Convert",
"date": "Date",
......
......@@ -3,7 +3,6 @@ import { fromJS } from 'immutable';
// Action constants
const SET_COMMUNITY_TITLE = 'community/SET_COMMUNITY_TITLE';
const SET_COMMUNITY_DESCRIPTION = 'community/SET_COMMUNITY_DESCRIPTION';
const SET_COMMUNITY_NSFW = 'community/SET_COMMUNITY_NSFW';
const SET_COMMUNITY_OWNER_ACCOUNT_NAME =
'community/SET_COMMUNITY_OWNER_ACCOUNT_NAME';
......@@ -28,7 +27,6 @@ const COMMUNITY_HIVEMIND_OPERATION_ERROR =
const defaultState = fromJS({
communityTitle: '',
communityDescription: '',
communityNSFW: false,
communityOwnerName: '',
communityOwnerWifPassword: '',
communityCreatePending: false,
......@@ -49,10 +47,6 @@ export default function reducer(state = defaultState, action) {
const description = fromJS(payload);
return state.merge({ communityDescription: description });
}
case SET_COMMUNITY_NSFW: {
const nsfw = fromJS(payload);
return state.merge({ communityNSFW: nsfw });
}
case SET_COMMUNITY_OWNER_ACCOUNT_NAME: {
const name = fromJS(payload);
return state.merge({ communityOwnerName: name });
......@@ -107,10 +101,6 @@ export const setCommunityDescription = payload => ({
type: SET_COMMUNITY_DESCRIPTION,
payload,
});
export const setCommunityNSFW = payload => ({
type: SET_COMMUNITY_NSFW,
payload,
});
export const setCommunityOwnerAccountName = payload => ({
type: SET_COMMUNITY_OWNER_ACCOUNT_NAME,
......
......@@ -20,7 +20,7 @@ const generateAuth = (user, pass, type) => {
};
};
const generateHivemindOperation = (action, params, actor_name) => {
const generateHivemindOperation = (actor_name, action, params) => {
return [
'custom_json',
{
......@@ -49,14 +49,16 @@ export function* customOps(action) {
accountName,
communityTitle,
communityDescription,
communityNSFW,
communityOwnerName,
communityOwnerWifPassword,
createAccountSuccessCB,
createAccountErrorCB,
broadcastOpsErrorCB,
} = action.payload;
yield call(wait, 9000);
// wait 3s for account creation to settle
yield call(wait, 3000);
try {
const communityOwnerPosting = auth.getPrivateKeys(
communityOwnerName,
......@@ -65,28 +67,33 @@ export function* customOps(action) {
);
const setRoleOperation = generateHivemindOperation(
communityOwnerName,
'setRole',
{
community: communityOwnerName,
account: accountName,
role: 'admin',
},
communityOwnerName,
communityOwnerPosting
}
);
const updatePropsOperation = generateHivemindOperation(
communityOwnerName,
'updateProps',
{
community: communityOwnerName,
props: {
title: communityTitle,
about: communityDescription,
is_nsfw: !!communityNSFW,
},
},
communityOwnerName,
communityOwnerPosting
}
);
const subscribeToCommunityOperation = generateHivemindOperation(
accountName,
'subscribe',
{
community: communityOwnerName,
}
);
yield broadcast.sendAsync(
......@@ -103,6 +110,23 @@ export function* customOps(action) {
]
);
// subscription op must be broadcast from logged in user
yield put(
transactionActions.broadcastOperation({
type: subscribeToCommunityOperation[0],
operation: subscribeToCommunityOperation[1],
successCallback: res => {
console.log('subscribed');
},
errorCallback: res => {
console.log('subscribe error', res);
},
})
);
// wait a few blocks for hivemind to index ops before alerting user
yield call(wait, 6000);
yield put({
type: communityActions.CREATE_COMMUNITY_SUCCESS,
payload: true,
......@@ -130,7 +154,6 @@ export function* createCommunityAccount(createCommunityAction) {
accountName,
communityTitle,
communityDescription,
communityNSFW,
communityOwnerName,
communityOwnerWifPassword,
broadcastOpsCb,
......@@ -175,7 +198,8 @@ export function* createCommunityAccount(createCommunityAction) {
yield put(
transactionActions.broadcastOperation({
type: 'account_create',
confirm: 'Are you sure?',
confirm:
'This operation will cost 3 STEEM. Would you like to proceed?',
operation: op,
successCallback: res => {
createAccountSuccessCB();
......@@ -183,7 +207,7 @@ export function* createCommunityAccount(createCommunityAction) {
},
errorCallback: res => {
console.log('error', res);
createAccountErrorCB();
createAccountErrorCB(res);
},
})
);
......
/* From:
http://difnet.com.br/opensource/unicode_hack.js
It has been significantly modified.
Modifications are copyright 2014 Makyen and released under the MPL 2.0.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*! unicode_hack.js
Copyright (C) 2010-2012 Marcelo Gibson de Castro Gonçalves. All rights reserved.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without any warranty.
*/
const Unicode = {
/* Strings to match Unicode characters in the BMP according to their Unicode category.
Extracted from Unicode specification, version 5.0.0, source:
http://unicode.org/versions/Unicode5.0.0/
*/
/*
Abbr Long Description
Lu Uppercase_Letter an uppercase letter
Ll Lowercase_Letter a lowercase letter
Lt Titlecase_Letter a digraphic character, with first part uppercase
LC Cased_Letter Lu | Ll | Lt
Lm Modifier_Letter a modifier letter
Lo Other_Letter other letters, including syllables and ideographs
L Letter Lu | Ll | Lt | Lm | Lo
Mn Nonspacing_Mark a nonspacing combining mark (zero advance width)
Mc Spacing_Mark a spacing combining mark (positive advance width)
Me Enclosing_Mark an enclosing combining mark
M Mark Mn | Mc | Me
Nd Decimal_Number a decimal digit
Nl Letter_Number a letterlike numeric character
No Other_Number a numeric character of other type
N Number Nd | Nl | No
Pc Connector_Punctuation a connecting punctuation mark, like a tie
Pd Dash_Punctuation a dash or hyphen punctuation mark
Ps Open_Punctuation an opening punctuation mark (of a pair)
Pe Close_Punctuation a closing punctuation mark (of a pair)
Pi Initial_Punctuation an initial quotation mark
Pf Final_Punctuation a final quotation mark
Po Other_Punctuation a punctuation mark of other type
P Punctuation Pc | Pd | Ps | Pe | Pi | Pf | Po
Sm Math_Symbol a symbol of mathematical use
Sc Currency_Symbol a currency sign
Sk Modifier_Symbol a non-letterlike modifier symbol
So Other_Symbol a symbol of other type
S Symbol Sm | Sc | Sk | So
Zs Space_Separator a space character (of various non-zero widths)
Zl Line_Separator U+2028 LINE SEPARATOR only
Zp Paragraph_Separator U+2029 PARAGRAPH SEPARATOR only
Z Separator Zs | Zl | Zp
Cc Control a C0 or C1 control code
Cf Format a format control character
Cs Surrogate a surrogate code point
Co Private_Use a private-use character
Cn Unassigned a reserved unassigned code point or a noncharacter
C Other Cc | Cf | Cs | Co | Cn
*/
/* Alpha Sorted
Abbr Long Description
C Other Cc | Cf | Cs | Co | Cn
Cc Control a C0 or C1 control code
Cf Format a format control character
Cn Unassigned a reserved unassigned code point or a noncharacter
Co Private_Use a private-use character
Cs Surrogate a surrogate code point
L Letter Lu | Ll | Lt | Lm | Lo
LC Cased_Letter Lu | Ll | Lt
Ll Lowercase_Letter a lowercase letter
Lm Modifier_Letter a modifier letter
Lo Other_Letter other letters, including syllables and ideographs
Lt Titlecase_Letter a digraphic character, with first part uppercase
Lu Uppercase_Letter an uppercase letter
M Mark Mn | Mc | Me
Mc Spacing_Mark a spacing combining mark (positive advance width)
Me Enclosing_Mark an enclosing combining mark
Mn Nonspacing_Mark a nonspacing combining mark (zero advance width)
N Number Nd | Nl | No
Nd Decimal_Number a decimal digit
Nl Letter_Number a letterlike numeric character
No Other_Number a numeric character of other type
P Punctuation Pc | Pd | Ps | Pe | Pi | Pf | Po
Pc Connector_Punctuation a connecting punctuation mark, like a tie
Pd Dash_Punctuation a dash or hyphen punctuation mark
Pe Close_Punctuation a closing punctuation mark (of a pair)