diff --git a/app/actions/account.js b/app/actions/account.js
index 7aabf36..d788aa0 100644
--- a/app/actions/account.js
+++ b/app/actions/account.js
@@ -4,6 +4,10 @@ import { Asset, Price, Client } from 'dsteem'
import type { accountStateType } from '../reducers/account';
import * as ProcessingActions from './processing';
+export const ACCOUNT_CUSTOM_JSON_STARTED = 'ACCOUNT_CUSTOM_JSON_STARTED';
+export const ACCOUNT_CUSTOM_JSON_RESOLVED = 'ACCOUNT_CUSTOM_JSON_RESOLVED';
+export const ACCOUNT_CUSTOM_JSON_FAILED = 'ACCOUNT_CUSTOM_JSON_FAILED';
+export const ACCOUNT_CUSTOM_JSON_COMPLETED = 'ACCOUNT_CUSTOM_JSON_COMPLETED';
export const ACCOUNT_DATA_MINIMUM_ACCOUNT_DELEGATION = 'ACCOUNT_DATA_MINIMUM_ACCOUNT_DELEGATION';
export const ACCOUNT_DATA_UPDATE = 'ACCOUNT_DATA_UPDATE';
export const ACCOUNT_DATA_UPDATE_FAILED = 'ACCOUNT_DATA_UPDATE_FAILED';
@@ -422,3 +426,32 @@ export function cancelWithdrawVesting(wif, params) {
});
};
}
+
+export function customJson(wif, params) {
+ return (dispatch: () => void) => {
+ const { account, id, json } = params
+ dispatch({
+ type: ACCOUNT_CUSTOM_JSON_STARTED
+ })
+ steem.broadcast.customJson(wif, [], [account], id, json, function(err, result) {
+ if(result) {
+ dispatch({
+ type: ACCOUNT_CUSTOM_JSON_RESOLVED
+ })
+ }
+ if(err) {
+ dispatch({
+ type: ACCOUNT_CUSTOM_JSON_FAILED,
+ payload: err
+ })
+ }
+ });
+ };
+}
+
+
+export function customJsonCompleted() {
+ return {
+ type: ACCOUNT_CUSTOM_JSON_COMPLETED,
+ }
+}
diff --git a/app/components/Accounts/CustomJSON.js b/app/components/Accounts/CustomJSON.js
new file mode 100644
index 0000000..89514ef
--- /dev/null
+++ b/app/components/Accounts/CustomJSON.js
@@ -0,0 +1,142 @@
+// @flow
+import React, { Component } from 'react';
+import { Grid, Header, Message, Segment, Select, Table } from 'semantic-ui-react';
+
+import { Form, Input } from 'formsy-semantic-ui-react';
+
+import AccountName from '../global/AccountName';
+
+export default class AccountsCustomJSON extends Component {
+ constructor(props) {
+ super(props)
+ this.state = {
+ account: props.keys.names[0],
+ id: '',
+ json: '',
+ message: false
+ }
+ }
+
+ resetState(extraState) {
+ this.setState(Object.assign({}, {
+ id: '',
+ json: ''
+ }, extraState))
+ }
+
+
+ handleIdChange = (e: SyntheticEvent, { value }: { value: any }) => {
+ this.setState({
+ id: value
+ })
+ }
+
+ handleJsonChange = (e: SyntheticEvent, { value }: { value: any }) => {
+ this.setState({
+ json: value.trim()
+ })
+ }
+
+ handleAccountChange = (e: SyntheticEvent, { value }: { value: any }) => {
+ this.setState({
+ account: value
+ })
+ }
+
+ onValidSubmit = (
+ e: SyntheticEvent
+ ) => {
+ const { account, id, json } = this.state
+ this.setState({message: false})
+ this.props.actions.useKey('customJson', { account, id, json }, this.props.keys.permissions[account]);
+ }
+
+ componentWillReceiveProps = (nextProps) => {
+ if (nextProps.processing.account_custom_json_resolved) {
+ nextProps.actions.customJsonCompleted();
+ this.resetState({
+ 'message': 'Your transaction was successfully broadcast.'
+ });
+ }
+ }
+
+ render() {
+ const {
+ account_custom_json_error,
+ account_custom_json_pending,
+ account_custom_json_resolved
+ } = this.props.processing;
+ const keys = this.props.keys;
+ const availableFrom = keys.names.map((name) => {
+ const hasPermission = (keys.permissions[name].type === 'active' || keys.permissions[name].type === 'owner');
+ return hasPermission ? {
+ key: name,
+ text: name,
+ value: name
+ } : {
+ key: name,
+ disabled: true,
+ text: name + ' (unavailable - active/owner key not loaded)'
+ };
+ });
+ let message = false
+ if(this.state.message) {
+ message = (
+