Only allow single click on buttons

Closes #544
This commit is contained in:
Ildar Kamalov 2019-01-16 14:51:17 +03:00
parent f1b6da93cf
commit e1bb428a6a
8 changed files with 80 additions and 24 deletions

View file

@ -352,11 +352,11 @@ export const refreshFiltersFailure = createAction('FILTERING_REFRESH_FAILURE');
export const refreshFiltersSuccess = createAction('FILTERING_REFRESH_SUCCESS');
export const refreshFilters = () => async (dispatch) => {
dispatch(refreshFiltersRequest);
dispatch(refreshFiltersRequest());
dispatch(showLoading());
try {
const refreshText = await apiClient.refreshFilters();
dispatch(refreshFiltersSuccess);
dispatch(refreshFiltersSuccess());
if (refreshText.includes('OK')) {
if (refreshText.includes('OK 0')) {

View file

@ -19,7 +19,9 @@ body {
}
.loading-bar {
position: absolute;
position: fixed;
top: 0;
left: 0;
z-index: 103;
height: 3px;
background: linear-gradient(45deg, rgba(99, 125, 120, 1) 0%, rgba(88, 177, 101, 1) 100%);

View file

@ -25,12 +25,17 @@ class Dashboard extends Component {
}
getToggleFilteringButton = () => {
const { protectionEnabled } = this.props.dashboard;
const { protectionEnabled, processingProtection } = this.props.dashboard;
const buttonText = protectionEnabled ? 'disable_protection' : 'enable_protection';
const buttonClass = protectionEnabled ? 'btn-gray' : 'btn-success';
return (
<button type="button" className={`btn btn-sm mr-2 ${buttonClass}`} onClick={() => this.props.toggleProtection(protectionEnabled)}>
<button
type="button"
className={`btn btn-sm mr-2 ${buttonClass}`}
onClick={() => this.props.toggleProtection(protectionEnabled)}
disabled={processingProtection}
>
<Trans>{buttonText}</Trans>
</button>
);
@ -125,6 +130,7 @@ Dashboard.propTypes = {
isCoreRunning: PropTypes.bool,
getFiltering: PropTypes.func,
toggleProtection: PropTypes.func,
processingProtection: PropTypes.bool,
t: PropTypes.func,
};

View file

@ -68,7 +68,7 @@ class Filters extends Component {
render() {
const { t } = this.props;
const { filters, userRules } = this.props.filtering;
const { filters, userRules, processingRefreshFilters } = this.props.filtering;
return (
<div>
<PageTitle title={ t('filters') } />
@ -95,8 +95,21 @@ class Filters extends Component {
noDataText={ t('no_filters_added') }
/>
<div className="card-actions">
<button className="btn btn-success btn-standart mr-2" type="submit" onClick={this.props.toggleFilteringModal}><Trans>add_filter_btn</Trans></button>
<button className="btn btn-primary btn-standart" type="submit" onClick={this.props.refreshFilters}><Trans>check_updates_btn</Trans></button>
<button
className="btn btn-success btn-standart mr-2"
type="submit"
onClick={this.props.toggleFilteringModal}
>
<Trans>add_filter_btn</Trans>
</button>
<button
className="btn btn-primary btn-standart"
type="submit"
onClick={this.props.refreshFilters}
disabled={processingRefreshFilters}
>
<Trans>check_updates_btn</Trans>
</button>
</div>
</Card>
</div>
@ -114,6 +127,7 @@ class Filters extends Component {
toggleModal={this.props.toggleFilteringModal}
addFilter={this.props.addFilter}
isFilterAdded={this.props.filtering.isFilterAdded}
processingAddFilter={this.props.filtering.processingAddFilter}
title={ t('new_filter_btn') }
inputDescription={ t('enter_valid_filter_url') }
/>
@ -130,6 +144,8 @@ Filters.propTypes = {
filters: PropTypes.array,
isFilteringModalOpen: PropTypes.bool.isRequired,
isFilterAdded: PropTypes.bool,
processingAddFilter: PropTypes.bool,
processingRefreshFilters: PropTypes.bool,
}),
removeFilter: PropTypes.func.isRequired,
toggleFilterStatus: PropTypes.func.isRequired,

View file

@ -77,6 +77,7 @@ class Logs extends Component {
type="button"
className={`btn btn-sm ${buttonClass}`}
onClick={() => this.toggleBlocking(buttonType, domain)}
disabled={this.props.filtering.processingRules}
>
<Trans>{buttonText}</Trans>
</button>
@ -269,7 +270,7 @@ class Logs extends Component {
saveAs(dataBlob, DOWNLOAD_LOG_FILENAME);
};
renderButtons(queryLogEnabled) {
renderButtons(queryLogEnabled, logStatusProcessing) {
if (queryLogEnabled) {
return (
<Fragment>
@ -277,6 +278,7 @@ class Logs extends Component {
className="btn btn-gray btn-sm mr-2"
type="submit"
onClick={() => this.props.toggleLogStatus(queryLogEnabled)}
disabled={logStatusProcessing}
><Trans>disabled_log_btn</Trans></button>
<button
className="btn btn-primary btn-sm mr-2"
@ -297,6 +299,7 @@ class Logs extends Component {
className="btn btn-success btn-sm mr-2"
type="submit"
onClick={() => this.props.toggleLogStatus(queryLogEnabled)}
disabled={logStatusProcessing}
><Trans>enabled_log_btn</Trans></button>
);
}
@ -308,7 +311,7 @@ class Logs extends Component {
<Fragment>
<PageTitle title={ t('query_log') } subtitle={ t('last_dns_queries') }>
<div className="page-title__actions">
{this.renderButtons(queryLogEnabled)}
{this.renderButtons(queryLogEnabled, dashboard.logStatusProcessing)}
</div>
</PageTitle>
<Card>
@ -332,6 +335,8 @@ Logs.propTypes = {
userRules: PropTypes.string,
setRules: PropTypes.func,
addSuccessToast: PropTypes.func,
processingRules: PropTypes.bool,
logStatusProcessing: PropTypes.bool,
t: PropTypes.func,
};

View file

@ -23,7 +23,7 @@ class Dhcp extends Component {
}
getToggleDhcpButton = () => {
const { config, active } = this.props.dhcp;
const { config, active, processingDhcp } = this.props.dhcp;
const activeDhcpFound = active && active.found;
const filledConfig = Object.keys(config).every((key) => {
if (key === 'enabled') {
@ -39,6 +39,7 @@ class Dhcp extends Component {
type="button"
className="btn btn-standart mr-2 btn-gray"
onClick={() => this.props.toggleDhcp(config)}
disabled={processingDhcp}
>
<Trans>dhcp_disable</Trans>
</button>
@ -50,7 +51,7 @@ class Dhcp extends Component {
type="button"
className="btn btn-standart mr-2 btn-success"
onClick={() => this.handleToggle(config)}
disabled={!filledConfig || activeDhcpFound}
disabled={!filledConfig || activeDhcpFound || processingDhcp}
>
<Trans>dhcp_enable</Trans>
</button>

View file

@ -55,6 +55,7 @@ class Modal extends Component {
isOpen,
title,
inputDescription,
processingAddFilter,
} = this.props;
const { isUrlValid, url, name } = this.state;
const inputUrlClass = classnames({
@ -71,8 +72,8 @@ class Modal extends Component {
if (!this.props.isFilterAdded) {
return (
<React.Fragment>
<input type="text" className={inputNameClass} placeholder={ this.props.t('enter_name_hint') } onChange={this.handleNameChange} />
<input type="text" className={inputUrlClass} placeholder={ this.props.t('enter_url_hint') } onChange={this.handleUrlChange} />
<input type="text" className={inputNameClass} placeholder={this.props.t('enter_name_hint')} onChange={this.handleNameChange} />
<input type="text" className={inputUrlClass} placeholder={this.props.t('enter_url_hint')} onChange={this.handleUrlChange} />
{inputDescription &&
<div className="description">
{inputDescription}
@ -93,7 +94,7 @@ class Modal extends Component {
<ReactModal
className="Modal__Bootstrap modal-dialog modal-dialog-centered"
closeTimeoutMS={0}
isOpen={ isOpen }
isOpen={isOpen}
onRequestClose={this.closeModal}
>
<div className="modal-content">
@ -106,14 +107,26 @@ class Modal extends Component {
</button>
</div>
<div className="modal-body">
{ renderBody()}
{renderBody()}
</div>
{
!this.props.isFilterAdded &&
<div className="modal-footer">
<button type="button" className="btn btn-secondary" onClick={this.closeModal}><Trans>cancel_btn</Trans></button>
<button type="button" className="btn btn-success" onClick={this.handleNext} disabled={isValidForSubmit}><Trans>add_filter_btn</Trans></button>
</div>
{!this.props.isFilterAdded &&
<div className="modal-footer">
<button
type="button"
className="btn btn-secondary"
onClick={this.closeModal}
>
<Trans>cancel_btn</Trans>
</button>
<button
type="button"
className="btn btn-success"
onClick={this.handleNext}
disabled={isValidForSubmit || processingAddFilter}
>
<Trans>add_filter_btn</Trans>
</button>
</div>
}
</div>
</ReactModal>
@ -128,6 +141,7 @@ Modal.propTypes = {
inputDescription: PropTypes.string,
addFilter: PropTypes.func.isRequired,
isFilterAdded: PropTypes.bool,
processingAddFilter: PropTypes.bool,
t: PropTypes.func,
};

View file

@ -140,8 +140,14 @@ const dashboard = handleActions({
return newState;
},
[actions.toggleProtectionRequest]: state => ({ ...state, processingProtection: true }),
[actions.toggleProtectionFailure]: state => ({ ...state, processingProtection: false }),
[actions.toggleProtectionSuccess]: (state) => {
const newState = { ...state, protectionEnabled: !state.protectionEnabled };
const newState = {
...state,
protectionEnabled: !state.protectionEnabled,
processingProtection: false,
};
return newState;
},
@ -164,6 +170,7 @@ const dashboard = handleActions({
processingFiltering: true,
upstreamDns: [],
protectionEnabled: false,
processingProtection: false,
});
const queryLogs = handleActions({
@ -228,6 +235,8 @@ const filtering = handleActions({
isFilteringModalOpen: false,
processingFilters: false,
processingRules: false,
processingAddFilter: false,
processingRefreshFilters: false,
filters: [],
userRules: '',
});
@ -291,16 +300,19 @@ const dhcp = handleActions({
processingStatus: false,
}),
[actions.toggleDhcpRequest]: state => ({ ...state, processingDhcp: true }),
[actions.toggleDhcpFailure]: state => ({ ...state, processingDhcp: false }),
[actions.toggleDhcpSuccess]: (state) => {
const { config } = state;
const newConfig = { ...config, enabled: !config.enabled };
const newState = { ...state, config: newConfig };
const newState = { ...state, config: newConfig, processingDhcp: false };
return newState;
},
}, {
processing: true,
processingStatus: false,
processingInterfaces: false,
processingDhcp: false,
config: {
enabled: false,
},