diff --git a/src/api/ShlinkApiClient.js b/src/api/ShlinkApiClient.js
index 7dac876e..767fc742 100644
--- a/src/api/ShlinkApiClient.js
+++ b/src/api/ShlinkApiClient.js
@@ -54,7 +54,12 @@ export class ShlinkApiClient {
.then(resp => resp.data.tags.data)
.catch(e => this._handleAuthError(e, this.listTags, []));
- _performRequest = async (url, method = 'GET', params = {}, data = {}) => {
+ deleteTags = tags =>
+ this._performRequest('/tags', 'DELETE', { tags })
+ .then(() => ({ tags }))
+ .catch(e => this._handleAuthError(e, this.deleteTag, []));
+
+ _performRequest = async (url, method = 'GET', query = {}, body = {}) => {
if (isEmpty(this._token)) {
this._token = await this._authenticate();
}
@@ -63,8 +68,8 @@ export class ShlinkApiClient {
method,
url: `${this._baseUrl}${url}`,
headers: { 'Authorization': `Bearer ${this._token}` },
- params,
- data,
+ params: query,
+ data: body,
paramsSerializer: params => qs.stringify(params, { arrayFormat: 'brackets' })
}).then(resp => {
// Save new token
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 96c904b1..84587ae5 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -8,6 +8,7 @@ import shortUrlCreationResultReducer from '../short-urls/reducers/shortUrlCreati
import shortUrlVisitsReducer from '../short-urls/reducers/shortUrlVisits';
import shortUrlTagsReducer from '../short-urls/reducers/shortUrlTags';
import tagsListReducer from '../tags/reducers/tagsList';
+import tagDeleteReducer from '../tags/reducers/tagDelete';
export default combineReducers({
servers: serversReducer,
@@ -18,4 +19,5 @@ export default combineReducers({
shortUrlVisits: shortUrlVisitsReducer,
shortUrlTags: shortUrlTagsReducer,
tagsList: tagsListReducer,
+ tagDelete: tagDeleteReducer,
});
diff --git a/src/tags/TagsList.js b/src/tags/TagsList.js
index b6b5e3b3..1ce60416 100644
--- a/src/tags/TagsList.js
+++ b/src/tags/TagsList.js
@@ -22,7 +22,11 @@ export class TagsList extends React.Component {
}
if (tagsList.error) {
- return
Error loading tags :(
;
+ return (
+
+
Error loading tags :(
+
+ );
}
const tagsCount = tagsList.tags.length;
diff --git a/src/tags/helpers/DeleteTagConfirmModal.js b/src/tags/helpers/DeleteTagConfirmModal.js
index b4df93ad..e4138df3 100644
--- a/src/tags/helpers/DeleteTagConfirmModal.js
+++ b/src/tags/helpers/DeleteTagConfirmModal.js
@@ -1,28 +1,74 @@
-import React from 'react';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap';
import PropTypes from 'prop-types';
+import { pick } from 'ramda';
+import { deleteTag, tagDeleteType } from '../reducers/tagDelete';
+import { listTags } from '../reducers/tagsList';
const propTypes = {
tag: PropTypes.string.isRequired,
toggle: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
+ deleteTag: PropTypes.func,
+ listTags: PropTypes.func,
+ tagDelete: tagDeleteType,
};
-export default function DeleteTagConfirmModal({ tag, toggle, isOpen }) {
- return (
-
-
- Delete tag
-
-
- Are you sure you want to delete tag {tag}?
-
-
-
-
-
-
- );
+export class DeleteTagConfirmModal extends Component {
+ doDelete = () => {
+ const { tag, toggle, deleteTag } = this.props;
+ deleteTag(tag).then(() => {
+ this.tagDeleted = true;
+ toggle();
+ }).catch(() => {});
+ };
+ onClosed = () => {
+ if (!this.tagDeleted) {
+ return;
+ }
+
+ this.props.listTags();
+ };
+
+ componentDidMount() {
+ this.tagDeleted = false;
+ }
+
+ render() {
+ const { tag, toggle, isOpen, tagDelete } = this.props;
+
+ return (
+
+
+ Delete tag
+
+
+ Are you sure you want to delete tag {tag}?
+ {tagDelete.error && (
+
+ Something went wrong while deleting the tag :(
+
+ )}
+
+
+
+
+
+
+ );
+ }
}
DeleteTagConfirmModal.propTypes = propTypes;
+
+export default connect(
+ pick(['tagDelete']),
+ { deleteTag, listTags }
+)(DeleteTagConfirmModal);
diff --git a/src/tags/reducers/tagDelete.js b/src/tags/reducers/tagDelete.js
new file mode 100644
index 00000000..373c1b2d
--- /dev/null
+++ b/src/tags/reducers/tagDelete.js
@@ -0,0 +1,52 @@
+import ShlinkApiClient from '../../api/ShlinkApiClient';
+import { curry } from 'ramda';
+import PropTypes from 'prop-types';
+
+const DELETE_TAG_START = 'shlink/deleteTag/DELETE_TAG_START';
+const DELETE_TAG_ERROR = 'shlink/deleteTag/DELETE_TAG_ERROR';
+const DELETE_TAG = 'shlink/deleteTag/DELETE_TAG';
+
+export const tagDeleteType = PropTypes.shape({
+ deleting: PropTypes.bool,
+ error: PropTypes.bool,
+});
+
+const defaultState = {
+ deleting: false,
+ error: false,
+};
+
+export default function reduce(state = defaultState, action) {
+ switch (action.type) {
+ case DELETE_TAG_START:
+ return {
+ deleting: true,
+ error: false,
+ };
+ case DELETE_TAG_ERROR:
+ return {
+ deleting: false,
+ error: true,
+ };
+ case DELETE_TAG:
+ return {
+ deleting: false,
+ error: false,
+ };
+ default:
+ return state;
+ }
+}
+
+export const _deleteTag = (ShlinkApiClient, tag) => async dispatch => {
+ dispatch({ type: DELETE_TAG_START });
+
+ try {
+ await ShlinkApiClient.deleteTags([tag]);
+ dispatch({ type: DELETE_TAG });
+ } catch (e) {
+ dispatch({ type: DELETE_TAG_ERROR });
+ throw e;
+ }
+};
+export const deleteTag = curry(_deleteTag)(ShlinkApiClient);
diff --git a/src/tags/reducers/tagsList.js b/src/tags/reducers/tagsList.js
index c5fe5ab0..d0229ec2 100644
--- a/src/tags/reducers/tagsList.js
+++ b/src/tags/reducers/tagsList.js
@@ -43,7 +43,6 @@ export const _listTags = ShlinkApiClient => async dispatch => {
dispatch({ tags, type: LIST_TAGS });
} catch (e) {
dispatch({ type: LIST_TAGS_ERROR });
- throw e;
}
};
export const listTags = () => _listTags(ShlinkApiClient);