diff --git a/src/app/components/modules/Settings.jsx b/src/app/components/modules/Settings.jsx index 967b8e8d4..f4464d365 100644 --- a/src/app/components/modules/Settings.jsx +++ b/src/app/components/modules/Settings.jsx @@ -1,193 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; import tt from 'counterpart'; -import * as userActions from 'app/redux/UserReducer'; -import * as transactionActions from 'app/redux/TransactionReducer'; import * as appActions from 'app/redux/AppReducer'; import o2j from 'shared/clash/object2json'; -import LoadingIndicator from 'app/components/elements/LoadingIndicator'; -import reactForm from 'app/utils/ReactForm'; -import UserList from 'app/components/elements/UserList'; -import Dropzone from 'react-dropzone'; class Settings extends React.Component { - constructor(props) { - super(props); - this.state = { - errorMessage: '', - successMessage: '', - progress: {}, - }; - this.initForm(props); - } - - initForm(props) { - reactForm({ - instance: this, - name: 'accountSettings', - fields: [ - 'profile_image', - 'cover_image', - 'name', - 'about', - 'location', - 'website', - ], - initialValues: props.profile, - validation: values => ({ - profile_image: - values.profile_image && - !/^https?:\/\//.test(values.profile_image) - ? tt('settings_jsx.invalid_url') - : null, - cover_image: - values.cover_image && - !/^https?:\/\//.test(values.cover_image) - ? tt('settings_jsx.invalid_url') - : null, - name: - values.name && values.name.length > 20 - ? tt('settings_jsx.name_is_too_long') - : values.name && /^\s*@/.test(values.name) - ? tt('settings_jsx.name_must_not_begin_with') - : null, - about: - values.about && values.about.length > 160 - ? tt('settings_jsx.about_is_too_long') - : null, - location: - values.location && values.location.length > 30 - ? tt('settings_jsx.location_is_too_long') - : null, - website: - values.website && values.website.length > 100 - ? tt('settings_jsx.website_url_is_too_long') - : values.website && !/^https?:\/\//.test(values.website) - ? tt('settings_jsx.invalid_url') - : null, - }), - }); - this.handleSubmitForm = this.state.accountSettings.handleSubmit(args => - this.handleSubmit(args) - ); - } - - onDrop = (acceptedFiles, rejectedFiles) => { - if (!acceptedFiles.length) { - if (rejectedFiles.length) { - this.setState({ - progress: { error: 'Please insert only image files.' }, - }); - console.log('onDrop Rejected files: ', rejectedFiles); - } - return; - } - const file = acceptedFiles[0]; - this.upload(file, file.name); - }; - - onOpenClick = imageName => { - this.setState({ - imageInProgress: imageName, - }); - this.dropzone.open(); - }; - - upload = (file, name = '') => { - const { uploadImage } = this.props; - this.setState({ - progress: { message: tt('settings_jsx.uploading_image') + '...' }, - }); - uploadImage(file, progress => { - if (progress.url) { - this.setState({ progress: {} }); - const { url } = progress; - const image_md = `${url}`; - let field; - if (this.state.imageInProgress === 'profile_image') { - field = this.state.profile_image; - } else if (this.state.imageInProgress === 'cover_image') { - field = this.state.cover_image; - } else { - return; - } - field.props.onChange(image_md); - } else { - this.setState({ progress }); - } - setTimeout(() => { - this.setState({ progress: {} }); - }, 4000); // clear message - }); - }; - - handleSubmit = ({ updateInitialValues }) => { - let { metaData } = this.props; - if (!metaData) metaData = {}; - if (!metaData.profile) metaData.profile = {}; - delete metaData.user_image; // old field... cleanup - - const { - profile_image, - cover_image, - name, - about, - location, - website, - } = this.state; - - // Update relevant fields - metaData.profile.profile_image = profile_image.value; - metaData.profile.cover_image = cover_image.value; - metaData.profile.name = name.value; - metaData.profile.about = about.value; - metaData.profile.location = location.value; - metaData.profile.website = website.value; - - // Remove empty keys - if (!metaData.profile.profile_image) - delete metaData.profile.profile_image; - if (!metaData.profile.cover_image) delete metaData.profile.cover_image; - if (!metaData.profile.name) delete metaData.profile.name; - if (!metaData.profile.about) delete metaData.profile.about; - if (!metaData.profile.location) delete metaData.profile.location; - if (!metaData.profile.website) delete metaData.profile.website; - - const { account, updateAccount } = this.props; - this.setState({ loading: true }); - updateAccount({ - json_metadata: JSON.stringify(metaData), - account: account.name, - memo_key: account.memo_key, - errorCallback: e => { - if (e === 'Canceled') { - this.setState({ - loading: false, - errorMessage: '', - }); - } else { - console.log('updateAccount ERROR', e); - this.setState({ - loading: false, - changed: false, - errorMessage: tt('g.server_returned_error'), - }); - } - }, - successCallback: () => { - this.setState({ - loading: false, - changed: false, - errorMessage: '', - successMessage: tt('settings_jsx.saved'), - }); - // remove successMessage after a while - setTimeout(() => this.setState({ successMessage: '' }), 4000); - updateInitialValues(); - }, - }); - }; - handleLanguageChange = event => { const locale = event.target.value; const userPreferences = { ...this.props.user_preferences, locale }; @@ -195,187 +12,29 @@ class Settings extends React.Component { }; render() { - const { state, props } = this; - - const { submitting, valid, touched } = this.state.accountSettings; - const disabled = state.loading || submitting || !valid || !touched; - - const { - profile_image, - cover_image, - name, - about, - location, - website, - progress, - } = this.state; - - const { account, user_preferences } = this.props; - + const { user_preferences } = this.props; return (

{tt('settings_jsx.preferences')}

- + {tt('g.choose_language')} +
-
-
- -
-
-

{tt('settings_jsx.public_profile_settings')}

- {progress.message && ( -
{progress.message}
- )} - {progress.error && ( -
- {tt('reply_editor.image_upload')} - {': '} - {progress.error} -
- )} - -
- {profile_image.blur && - profile_image.touched && - profile_image.error} -
- -
- {cover_image.blur && - cover_image.touched && - cover_image.error} -
- -
- {name.touched && name.error} -
- -
- {about.touched && about.error} -
- -
- {location.touched && location.error} -
- -
- {website.blur && website.touched && website.error} -
-
- {state.loading && ( - - -
-
- )} - {!state.loading && ( - - )}{' '} - {state.errorMessage ? ( - - {state.errorMessage} - - ) : state.successMessage ? ( - - {state.successMessage} - - ) : null} -
-
); } @@ -384,44 +43,16 @@ class Settings extends React.Component { export default connect( // mapStateToProps (state, ownProps) => { - const { accountname } = ownProps.routeParams; - const account = state.global.getIn(['accounts', accountname]).toJS(); - const current_user = state.user.get('current'); - let metaData = account - ? o2j.ifStringParseJSON(account.json_metadata) - : {}; - if (typeof metaData === 'string') - metaData = o2j.ifStringParseJSON(metaData); // issue #1237 - const profile = metaData && metaData.profile ? metaData.profile : {}; const user_preferences = state.app.get('user_preferences').toJS(); - return { - account, - metaData, - accountname, - profile, user_preferences, ...ownProps, }; }, // mapDispatchToProps dispatch => ({ - changeLanguage: language => { - dispatch(userActions.changeLanguage(language)); - }, setUserPreferences: payload => { dispatch(appActions.setUserPreferences(payload)); }, - uploadImage: (file, progress) => - dispatch(userActions.uploadImage({ file, progress })), - updateAccount: ({ successCallback, errorCallback, ...operation }) => { - const options = { - type: 'account_update', - operation, - successCallback, - errorCallback, - }; - dispatch(transactionActions.broadcastOperation(options)); - }, }) )(Settings); diff --git a/src/app/redux/UserReducer.js b/src/app/redux/UserReducer.js index cd81e6bb7..a2da35bba 100644 --- a/src/app/redux/UserReducer.js +++ b/src/app/redux/UserReducer.js @@ -405,11 +405,6 @@ export const loadSavingsWithdraw = payload => ({ payload, }); -export const uploadImage = payload => ({ - type: UPLOAD_IMAGE, - payload, -}); - export const showSidePanel = () => ({ type: SHOW_SIDE_PANEL, }); diff --git a/src/app/redux/UserSaga.js b/src/app/redux/UserSaga.js index f43bca3a0..013539517 100644 --- a/src/app/redux/UserSaga.js +++ b/src/app/redux/UserSaga.js @@ -34,7 +34,6 @@ export const userWatches = [ takeLatest(userActions.LOGOUT, logout), takeLatest(userActions.LOGIN_ERROR, loginError), takeLatest(userActions.LOAD_SAVINGS_WITHDRAW, loadSavingsWithdraw), - takeLatest(userActions.UPLOAD_IMAGE, uploadImage), takeLatest(userActions.ACCEPT_TERMS, function*() { try { yield call(acceptTos); @@ -585,129 +584,3 @@ function* lookupPreviousOwnerAuthority({ payload: {} }) { // console.log('UserSage ---> previous_owner_authority', previous_owner_authority.toJS()) yield put(userActions.setUser({ previous_owner_authority })); } - -function* uploadImage({ - payload: { file, dataUrl, filename = 'image.txt', progress }, -}) { - const _progress = progress; - progress = msg => { - // console.log('Upload image progress', msg) - _progress(msg); - }; - - const stateUser = yield select(state => state.user); - const username = stateUser.getIn(['current', 'username']); - const keychainLogin = isLoggedInWithKeychain(); - const hasPosting = stateUser.getIn([ - 'current', - 'private_keys', - 'posting_private', - ]); - const hasActive = stateUser.getIn([ - 'current', - 'private_keys', - 'active_private', - ]); - - if (!username) { - progress({ error: 'Please login first.' }); - return; - } - - if (!(keychainLogin || hasPosting || hasActive)) { - // we still allow upload with the posting key, but it will prompt for active to update either way - // (due to current blockchain rules) - progress({ error: 'Login with your active key' }); - return; - } - - if (!file && !dataUrl) { - console.error('uploadImage required: file or dataUrl'); - return; - } - - let data, dataBs64; - if (file) { - // drag and drop - const reader = new FileReader(); - data = yield new Promise(resolve => { - reader.addEventListener('load', () => { - const result = new Buffer(reader.result, 'binary'); - resolve(result); - }); - reader.readAsBinaryString(file); - }); - } else { - // recover from preview - const commaIdx = dataUrl.indexOf(','); - dataBs64 = dataUrl.substring(commaIdx + 1); - data = new Buffer(dataBs64, 'base64'); - } - - // The challenge needs to be prefixed with a constant (both on the server and checked on the client) to make sure the server can't easily make the client sign a transaction doing something else. - const prefix = new Buffer('ImageSigningChallenge'); - const buf = Buffer.concat([prefix, data]); - const bufSha = hash.sha256(buf); - - const formData = new FormData(); - if (file) { - formData.append('file', file); - } else { - // formData.append('file', file, filename) <- Failed to add filename=xxx to Content-Disposition - // Can't easily make this look like a file so this relies on the server supporting: filename and filebinary - formData.append('filename', filename); - formData.append('filebase64', dataBs64); - } - - let sig; - if (keychainLogin) { - const response = yield new Promise(resolve => { - window.steem_keychain.requestSignBuffer( - username, - JSON.stringify(buf), - 'Posting', - response => { - resolve(response); - } - ); - }); - if (response.success) { - sig = response.result; - } else { - progress({ error: response.message }); - return; - } - } else { - sig = Signature.signBufferSha256( - bufSha, - hasPosting ? hasPosting : hasActive - ).toHex(); - } - const postUrl = `${$STM_Config.upload_image}/${username}/${sig}`; - - const xhr = new XMLHttpRequest(); - xhr.open('POST', postUrl); - xhr.onload = function() { - console.log(xhr.status, xhr.responseText); - const res = JSON.parse(xhr.responseText); - const { error } = res; - if (error) { - progress({ error: 'Error: ' + error }); - return; - } - const { url } = res; - progress({ url }); - }; - xhr.onerror = function(error) { - console.error(filename, error); - progress({ error: 'Unable to contact the server.' }); - }; - xhr.upload.onprogress = function(event) { - if (event.lengthComputable) { - const percent = Math.round((event.loaded / event.total) * 100); - progress({ message: `Uploading ${percent}%` }); - // console.log('Upload', percent) - } - }; - xhr.send(formData); -}