feat(cockpit/editor): add status messages for file operations

This commit is contained in:
Jonathan Rainville 2019-03-08 15:06:54 -05:00
parent 3f488e1d88
commit ecdfd473df
4 changed files with 53 additions and 6 deletions

View File

@ -7,6 +7,10 @@ import FontAwesomeIcon from 'react-fontawesome';
import AddFileModal from '../components/AddFileModal'; import AddFileModal from '../components/AddFileModal';
import AddFolderModal from '../components/AddFolderModal'; import AddFolderModal from '../components/AddFolderModal';
const StatusText = ({message, icon, spin = false}) => (
<span className="ml-2"><FontAwesomeIcon className="mr-1" name={icon} spin={spin}/>{message}</span>
);
export const TextEditorToolbarTabs = { export const TextEditorToolbarTabs = {
Interact: { label: 'Interact', icon: 'bolt' }, Interact: { label: 'Interact', icon: 'bolt' },
Details: { label: 'Details', icon: 'info-circle' }, Details: { label: 'Details', icon: 'info-circle' },
@ -18,10 +22,22 @@ export const TextEditorToolbarTabs = {
class TextEditorToolbar extends Component { class TextEditorToolbar extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
successMessage: ''
};
this.addFileModal = React.createRef(); this.addFileModal = React.createRef();
this.addFolderModal = React.createRef(); this.addFolderModal = React.createRef();
} }
componentDidUpdate(prevProps) {
if (this.props.editorOperationStatus.success !== prevProps.editorOperationStatus.success) {
this.setState({successMessage: this.props.editorOperationStatus.success});
setTimeout(() => {
this.setState({successMessage: ''});
}, 3000);
}
}
isActiveTab(tab) { isActiveTab(tab) {
return this.props.activeTab === tab; return this.props.activeTab === tab;
} }
@ -64,6 +80,9 @@ class TextEditorToolbar extends Component {
<FontAwesomeIcon className="mr-2" name="trash"/> <FontAwesomeIcon className="mr-2" name="trash"/>
Delete Delete
</Button> </Button>
{this.state.successMessage && <StatusText message={this.state.successMessage} icon="check"/>}
{this.props.editorOperationStatus.loading && <StatusText message="Processing..." icon="spinner" spin={true}/>}
{this.props.editorOperationStatus.error && <StatusText message={this.props.editorOperationStatus.error} icon="exclamation-triangle"/>}
</li> </li>
<li className="breadcrumb-menu"> <li className="breadcrumb-menu">
<Nav className="btn-group"> <Nav className="btn-group">
@ -87,7 +106,8 @@ TextEditorToolbar.propTypes = {
remove: PropTypes.func, remove: PropTypes.func,
toggleShowHiddenFiles: PropTypes.func, toggleShowHiddenFiles: PropTypes.func,
toggleAsideTab: PropTypes.func, toggleAsideTab: PropTypes.func,
activeTab: PropTypes.object activeTab: PropTypes.object,
editorOperationStatus: PropTypes.object
}; };
export default TextEditorToolbar; export default TextEditorToolbar;

View File

@ -8,7 +8,7 @@ import {
removeFile as removeFileAction, removeFile as removeFileAction,
saveFolder as saveFolderAction saveFolder as saveFolderAction
} from '../actions'; } from '../actions';
import { getRootDirname, getTheme } from '../reducers/selectors'; import { getRootDirname, getTheme, getEditorOperationStatus } from '../reducers/selectors';
class TextEditorToolbarContainer extends Component { class TextEditorToolbarContainer extends Component {
save() { save() {
@ -29,12 +29,14 @@ class TextEditorToolbarContainer extends Component {
saveFolder={this.props.saveFolder} saveFolder={this.props.saveFolder}
rootDirname={this.props.rootDirname} rootDirname={this.props.rootDirname}
remove={() => this.remove()} remove={() => this.remove()}
editorOperationStatus={this.props.editorOperationStatus}
activeTab={this.props.activeTab} />; activeTab={this.props.activeTab} />;
} }
} }
TextEditorToolbarContainer.propTypes = { TextEditorToolbarContainer.propTypes = {
currentFile: PropTypes.object, currentFile: PropTypes.object,
editorOperationStatus: PropTypes.object,
theme: PropTypes.string, theme: PropTypes.string,
isContract: PropTypes.bool, isContract: PropTypes.bool,
saveFile: PropTypes.func, saveFile: PropTypes.func,
@ -49,9 +51,10 @@ TextEditorToolbarContainer.propTypes = {
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { return {
rootDirname: getRootDirname(state), rootDirname: getRootDirname(state),
theme: getTheme(state) theme: getTheme(state),
editorOperationStatus: getEditorOperationStatus(state)
} }
} };
export default connect( export default connect(
mapStateToProps, mapStateToProps,

View File

@ -2,7 +2,8 @@ import {combineReducers} from 'redux';
import {REQUEST, SUCCESS, FAILURE, CONTRACT_COMPILE, FILES, LOGOUT, AUTHENTICATE, import {REQUEST, SUCCESS, FAILURE, CONTRACT_COMPILE, FILES, LOGOUT, AUTHENTICATE,
FETCH_CREDENTIALS, UPDATE_BASE_ETHER, CHANGE_THEME, FETCH_THEME, EXPLORER_SEARCH, DEBUGGER_INFO, FETCH_CREDENTIALS, UPDATE_BASE_ETHER, CHANGE_THEME, FETCH_THEME, EXPLORER_SEARCH, DEBUGGER_INFO,
SIGN_MESSAGE, VERIFY_MESSAGE, TOGGLE_BREAKPOINT, UPDATE_PREVIEW_URL, SIGN_MESSAGE, VERIFY_MESSAGE, TOGGLE_BREAKPOINT, UPDATE_PREVIEW_URL,
UPDATE_DEPLOYMENT_PIPELINE, WEB3_CONNECT, WEB3_DEPLOY, WEB3_ESTIMAGE_GAS, FETCH_EDITOR_TABS} from "../actions"; UPDATE_DEPLOYMENT_PIPELINE, WEB3_CONNECT, WEB3_DEPLOY, WEB3_ESTIMAGE_GAS, FETCH_EDITOR_TABS,
SAVE_FILE, SAVE_FOLDER, REMOVE_FILE} from "../actions";
import {EMBARK_PROCESS_NAME, DARK_THEME, DEPLOYMENT_PIPELINES, DEFAULT_HOST, ELEMENTS_LIMIT} from '../constants'; import {EMBARK_PROCESS_NAME, DARK_THEME, DEPLOYMENT_PIPELINES, DEFAULT_HOST, ELEMENTS_LIMIT} from '../constants';
const BN_FACTOR = 10000; const BN_FACTOR = 10000;
@ -376,6 +377,24 @@ function previewUrl(state= `${window.location.protocol}//${window.location.host}
return state; return state;
} }
const editorOperations = [SAVE_FILE, SAVE_FOLDER, REMOVE_FILE];
function editorOperationStatus(state = {error: '', success: '', loading: false}, action) {
// Success check
if (editorOperations.find(operation => operation[SUCCESS] === action.type)) {
return {error: '', success: 'Operation successful', loading: false};
}
// Error check
if (editorOperations.find(operation => operation[FAILURE] === action.type)) {
const message = action.error ? action.error.message || action.error : '';
return {error: 'Error during the process ' + message, success: '', loading: false};
}
// Loading check
if (editorOperations.find(operation => operation[REQUEST] === action.type)) {
return {error: '', success: '', loading: true};
}
return state;
}
const rootReducer = combineReducers({ const rootReducer = combineReducers({
entities, entities,
loading, loading,
@ -393,7 +412,8 @@ const rootReducer = combineReducers({
debuggerInfo, debuggerInfo,
theme, theme,
editorTabs, editorTabs,
previewUrl previewUrl,
editorOperationStatus
}); });
export default rootReducer; export default rootReducer;

View File

@ -246,3 +246,7 @@ export function getPreviewUrl(state) {
return state.previewUrl; return state.previewUrl;
} }
export function getEditorOperationStatus(state) {
return state.editorOperationStatus;
}