CreateCommunity.jsx 9.88 KB
Newer Older
1
import React from 'react';
/ /\ / /\/'s avatar
/ /\ / /\/ committed
2
import { APP_NAME } from 'app/client_config';
/ /\ / /\/'s avatar
/ /\ / /\/ committed
3
import { connect } from 'react-redux';
4
import * as communityActions from 'app/redux/CommunityReducer';
/ /\ / /\/'s avatar
/ /\ / /\/ committed
5
import tt from 'counterpart';
6
import { key_utils } from '@steemit/steem-js/lib/auth/ecc';
roadscape's avatar
roadscape committed
7
import LoadingIndicator from 'app/components/elements/LoadingIndicator';
roadscape's avatar
roadscape committed
8
import Unicode from 'app/utils/Unicode';
/ /\ / /\/'s avatar
/ /\ / /\/ committed
9

10 11 12 13 14 15
class CreateCommunity extends React.Component {
    constructor() {
        super();
        this.state = {
            accountError: false,
            broadcastOpsError: false,
/ /\ / /\/'s avatar
/ /\ / /\/ committed
16
            accountCreated: false,
17 18 19 20 21
        };
    }
    componentDidMount() {}
    render() {
        const {
/ /\ / /\/'s avatar
/ /\ / /\/ committed
22
            accountName,
23 24 25 26
            communityCreateError,
            communityCreatePending,
            communityCreateSuccess,
            createCommunity,
/ /\ / /\/'s avatar
/ /\ / /\/ committed
27
            communityDescription,
28
            communityOwnerWifPassword,
29 30 31 32 33 34
            communityOwnerName,
            communityTitle,
            updateCommunityTitle,
            updateCommunityDescription,
            updateCommunityOwnerAccountName,
            updateCommunityOwnerWifPassword,
/ /\ / /\/'s avatar
/ /\ / /\/ committed
35
            broadcastOps,
36
            communityCreationPending,
roadscape's avatar
roadscape committed
37
            socialUrl,
38 39
        } = this.props;

40 41 42 43 44 45
        const handleAccountCreateError = error => {
            // If the user cancels the account creation do not show an error.
            if (error === undefined) {
                communityCreationPending(false);
                return;
            }
/ /\ / /\/'s avatar
/ /\ / /\/ committed
46 47 48 49 50 51 52 53 54 55 56
            this.setState({ accountError: true });
        };

        const handleAccountCreateSuccess = () => {
            this.setState({ accountCreated: true });
        };

        const handleBroadcastOpsError = () => {
            this.setState({ broadcastOpsError: false });
        };

57 58 59 60 61
        const handleCommunityTitleInput = e => {
            if (e.target.value.length > 32) {
                return;
            }
            updateCommunityTitle(e.target.value);
/ /\ / /\/'s avatar
/ /\ / /\/ committed
62
        };
63

64 65 66 67 68 69 70 71 72
        const handleCommunityDescriptionInput = e => {
            if (e.target.value.length > 120) {
                return;
            }
            updateCommunityDescription(e.target.value);
        };

        const handleCommunitySubmit = e => {
            e.preventDefault();
/ /\ / /\/'s avatar
/ /\ / /\/ committed
73
            const createCommunityPayload = {
74 75 76 77 78
                accountName,
                communityTitle,
                communityDescription,
                communityOwnerName,
                communityOwnerWifPassword,
/ /\ / /\/'s avatar
/ /\ / /\/ committed
79 80 81
                createAccountSuccessCB: handleAccountCreateSuccess,
                createAccountErrorCB: handleAccountCreateError,
                broadcastOpsErrorCB: handleBroadcastOpsError,
82
            };
/ /\ / /\/'s avatar
/ /\ / /\/ committed
83 84 85 86 87
            if (!this.state.accountCreated) {
                createCommunity(createCommunityPayload);
            } else {
                broadcastOps(createCommunityPayload);
            }
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
        };

        const generateCommunityOwnerName = () => {
            return `hive-${Math.floor(Math.random() * 100000) + 100000}`;
        };

        const generateCreatorWifPassword = () => {
            return 'P' + key_utils.get_random_key().toWif();
        };

        const generateWif = () => {
            const wif = generateCreatorWifPassword();
            updateCommunityOwnerWifPassword(wif);
        };

        const generateUsername = () => {
            const ownerUsername = generateCommunityOwnerName();
            updateCommunityOwnerAccountName(ownerUsername);
        };

roadscape's avatar
roadscape committed
108
        const generateCreds = () => {
109 110 111 112 113
            generateWif();
            generateUsername();
        };

        const generateCommunityCredentialsButton = (
roadscape's avatar
roadscape committed
114 115
            <button type="button" className="button" onClick={generateCreds}>
                Next
116 117 118
            </button>
        );

roadscape's avatar
roadscape committed
119
        const credentialsPane = (
120
            <div>
roadscape's avatar
roadscape committed
121 122 123
                <label>
                    Owner Name / Password
                    <code className="pwd">
124
                        {communityOwnerName}
roadscape's avatar
roadscape committed
125
                        <br />
126 127
                        {communityOwnerWifPassword}
                    </code>
roadscape's avatar
roadscape committed
128 129 130 131 132
                </label>
                <label style={{ marginTop: '0px' }}>
                    <input type="checkbox" name="box2" required />
                    I have securely saved my owner name and password.
                </label>
133 134 135
            </div>
        );

roadscape's avatar
roadscape committed
136 137 138 139 140 141 142
        const submitCreateCommunityFormButton = error => (
            <input
                className="button"
                type="submit"
                value="Create Community"
                disabled={!!error}
            />
143 144
        );

roadscape's avatar
roadscape committed
145
        const hasPass = communityOwnerWifPassword.length > 0;
roadscape's avatar
roadscape committed
146 147 148 149 150 151

        let formError = null;
        const rx = new RegExp('^[' + Unicode.L + ']');
        if (!rx.test(communityTitle) && (communityTitle || hasPass))
            formError = 'Must start with a letter.';

roadscape's avatar
roadscape committed
152 153 154 155
        const form = (
            <form className="community--form" onSubmit={handleCommunitySubmit}>
                <div>{tt('g.community_create')}</div>
                <label>
156 157 158 159
                    Title
                    <input
                        id="community_title"
                        type="text"
roadscape's avatar
roadscape committed
160 161
                        minLength="3"
                        maxLength="20"
162 163 164 165 166
                        onChange={handleCommunityTitleInput}
                        value={communityTitle}
                        required
                    />
                </label>
roadscape's avatar
roadscape committed
167
                {formError && <span className="error">{formError}</span>}
roadscape's avatar
roadscape committed
168
                <label>
169 170 171 172
                    {tt('g.community_description')}
                    <input
                        id="community_description"
                        type="text"
roadscape's avatar
roadscape committed
173
                        maxLength="120"
174 175 176 177
                        onChange={handleCommunityDescriptionInput}
                        value={communityDescription}
                    />
                </label>
roadscape's avatar
roadscape committed
178 179
                {!hasPass && generateCommunityCredentialsButton}
                {hasPass && credentialsPane}
roadscape's avatar
roadscape committed
180
                {hasPass && submitCreateCommunityFormButton(formError)}
181 182
            </form>
        );
roadscape's avatar
roadscape committed
183 184 185 186 187 188 189 190 191 192

        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) {
roadscape's avatar
roadscape committed
193
            const url = `${socialUrl}/trending/${communityOwnerName}`;
roadscape's avatar
roadscape committed
194
            return (
roadscape's avatar
roadscape committed
195 196 197 198 199 200 201
                <div className="row">
                    <div className="column large-6 small-12">
                        Your community was created!<br />
                        <strong>
                            <a href={url}>Get started.</a>
                        </strong>
                    </div>
roadscape's avatar
roadscape committed
202 203 204 205 206
                </div>
            );
        }

        const showErr = msg => <div className="community--error">{msg}</div>;
roadscape's avatar
roadscape committed
207
        const adminMsg = `Account created. Setting @${accountName} as admin...`;
roadscape's avatar
roadscape committed
208

209 210 211
        return (
            <div className="row">
                <div className="column large-6 small-12">
roadscape's avatar
roadscape committed
212 213 214 215 216
                    {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}
217 218 219 220 221
                </div>
            </div>
        );
    }
}
/ /\ / /\/'s avatar
/ /\ / /\/ committed
222 223

export default connect(
224
    // mapStateToProps
/ /\ / /\/'s avatar
/ /\ / /\/ committed
225 226 227 228 229 230
    (state, ownProps) => {
        const { account } = ownProps;
        const accountName = account.get('name');
        const current = state.user.get('current');
        const username = current && current.get('username');
        const isMyAccount = username === accountName;
roadscape's avatar
roadscape committed
231
        const socialUrl = state.app.get('socialUrl');
232 233 234 235 236
        return {
            ...ownProps,
            ...state.community.toJS(),
            isMyAccount,
            accountName,
roadscape's avatar
roadscape committed
237
            socialUrl,
238
        };
/ /\ / /\/'s avatar
/ /\ / /\/ committed
239
    },
240 241 242 243 244 245 246 247 248
    // mapDispatchToProps
    dispatch => {
        return {
            updateCommunityTitle: title => {
                dispatch(communityActions.setCommunityTitle(title));
            },
            updateCommunityDescription: description => {
                dispatch(communityActions.setCommunityDescription(description));
            },
/ /\ / /\/'s avatar
/ /\ / /\/ committed
249 250 251 252 253 254 255 256 257 258
            updateCommunityOwnerAccountName: accountName => {
                dispatch(
                    communityActions.setCommunityOwnerAccountName(accountName)
                );
            },
            updateCommunityOwnerWifPassword: password => {
                dispatch(
                    communityActions.setCommunityOwnerWifPassword(password)
                );
            },
/ /\ / /\/'s avatar
/ /\ / /\/ committed
259
            createCommunity: createCommunityPayload => {
260 261
                const successCallback = () =>
                    dispatch(
/ /\ / /\/'s avatar
/ /\ / /\/ committed
262 263 264 265 266
                        communityActions.communityHivemindOperation(
                            createCommunityPayload
                        )
                    );
                const payload = {
/ /\ / /\/'s avatar
/ /\ / /\/ committed
267
                    broadcastOpsCb: successCallback,
/ /\ / /\/'s avatar
/ /\ / /\/ committed
268 269
                    ...createCommunityPayload,
                };
270
                dispatch(communityActions.createCommunity(payload));
/ /\ / /\/'s avatar
/ /\ / /\/ committed
271
            },
/ /\ / /\/'s avatar
/ /\ / /\/ committed
272 273 274 275 276 277 278
            broadcastOps: createCommunityPayload => {
                dispatch(
                    communityActions.communityHivemindOperation(
                        createCommunityPayload
                    )
                );
            },
279 280 281 282 283 284 285
            communityCreationPending: createCommunityAccountPending => {
                dispatch(
                    communityActions.createCommunityAccountPending(
                        createCommunityAccountPending
                    )
                );
            },
286 287
        };
    }
/ /\ / /\/'s avatar
/ /\ / /\/ committed
288
)(CreateCommunity);