diff --git a/src/api/ShlinkApiClient.js b/src/api/ShlinkApiClient.js index b435db1a..7630c322 100644 --- a/src/api/ShlinkApiClient.js +++ b/src/api/ShlinkApiClient.js @@ -25,30 +25,21 @@ export class ShlinkApiClient { * @returns {Promise} */ listShortUrls = (params = {}) => { - const { page = 1 } = params; - return this._performRequest(`/rest/short-codes?page=${page}`) + return this._performRequest('/rest/short-codes', 'GET', params) .then(resp => resp.data.shortUrls.data) - .catch(e => { - // If auth failed, reset token to force it to be regenerated, and perform a new request - if (e.response.status === 401) { - this._token = ''; - return this.listShortUrls(params); - } - - // Otherwise, let caller handle the rejection - return Promise.reject(e); - }); + .catch(e => this._handleAuthError(e, this.listShortUrls, [params])); }; - _performRequest = async (url, method = 'GET') => { + _performRequest = async (url, method = 'GET', params = {}) => { if (isEmpty(this._token)) { - this._token = await this._handleAuth(); + this._token = await this._authenticate(); } return await this.axios({ method, url: `${this._baseUrl}${url}`, - headers: { 'Authorization': `Bearer ${this._token}` } + headers: { 'Authorization': `Bearer ${this._token}` }, + params }).then(resp => { // Save new token const { authorization = '' } = resp.headers; @@ -57,7 +48,7 @@ export class ShlinkApiClient { }); }; - _handleAuth = async () => { + _authenticate = async () => { const resp = await this.axios({ method: 'POST', url: `${this._baseUrl}/rest/authenticate`, @@ -65,6 +56,17 @@ export class ShlinkApiClient { }); return resp.data.token; }; + + _handleAuthError = (e, method, args) => { + // If auth failed, reset token to force it to be regenerated, and perform a new request + if (e.response.status === 401) { + this._token = ''; + return method(...args); + } + + // Otherwise, let caller handle the rejection + return Promise.reject(e); + }; } export default new ShlinkApiClient(axios); diff --git a/src/reducers/index.js b/src/reducers/index.js index ee533601..b7a9af04 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -3,9 +3,11 @@ import { combineReducers } from 'redux'; import serversReducer from '../servers/reducers/server'; import selectedServerReducer from '../servers/reducers/selectedServer'; import shortUrlsListReducer from '../short-urls/reducers/shortUrlsList'; +import shortUrlsListParamsReducer from '../short-urls/reducers/shortUrlsListParams'; export default combineReducers({ servers: serversReducer, selectedServer: selectedServerReducer, shortUrlsList: shortUrlsListReducer, + shortUrlsListParams: shortUrlsListParamsReducer, }); diff --git a/src/reducers/types.js b/src/reducers/types.js index 7a3ab9e4..bec3df10 100644 --- a/src/reducers/types.js +++ b/src/reducers/types.js @@ -6,3 +6,4 @@ export const CREATE_SERVER = 'shlink/CREATE_SERVER'; // Short URLs export const LIST_SHORT_URLS = 'shlink/LIST_SHORT_URLS'; +export const UPDATE_SHORT_URLS_LIST = 'shlink/UPDATE_SHORT_URLS_LIST'; diff --git a/src/short-urls/SearchBar.js b/src/short-urls/SearchBar.js index 5a917a10..feb60af8 100644 --- a/src/short-urls/SearchBar.js +++ b/src/short-urls/SearchBar.js @@ -1,15 +1,57 @@ -import React from 'react'; import searchIcon from '@fortawesome/fontawesome-free-solid/faSearch'; import FontAwesomeIcon from '@fortawesome/react-fontawesome'; +import React from 'react'; +import { connect } from 'react-redux'; +import { updateShortUrlsList } from './reducers/shortUrlsList'; import './SearchBar.scss'; -export default class SearchBar extends React.Component { +export class SearchBar extends React.Component { + state = { + showClearBtn: false, + searchTerm: '', + }; + timer = null; + render() { return (
- + this.searchTermChanged(e.target.value)} + value={this.state.searchTerm} + /> +
); } + + searchTermChanged(searchTerm) { + this.setState({ + showClearBtn: searchTerm !== '', + searchTerm: searchTerm, + }); + + const resetTimer = () => { + clearTimeout(this.timer); + this.timer = null; + }; + resetTimer(); + + this.timer = setTimeout(() => { + this.props.updateShortUrlsList({ ...this.props.shortUrlsListParams, searchTerm }); + resetTimer(); + }, 500); + } } + +export default connect(state => ( + { shortUrlsListParams: state.shortUrlsListParams } +), { updateShortUrlsList })(SearchBar); diff --git a/src/short-urls/SearchBar.scss b/src/short-urls/SearchBar.scss index 66d24140..3e042a55 100644 --- a/src/short-urls/SearchBar.scss +++ b/src/short-urls/SearchBar.scss @@ -6,6 +6,7 @@ .search-bar__input.search-bar__input { padding-left: 40px; + padding-right: 40px; } .search-bar__icon { @@ -13,3 +14,8 @@ left: 15px; color: #707581; } + +.search-bar__close { + @include vertical-align(); + right: 15px; +} diff --git a/src/short-urls/ShortUrlsList.js b/src/short-urls/ShortUrlsList.js index 86f51341..e2b8ef32 100644 --- a/src/short-urls/ShortUrlsList.js +++ b/src/short-urls/ShortUrlsList.js @@ -16,7 +16,14 @@ import './ShortUrlsList.scss'; export class ShortUrlsList extends React.Component { componentDidMount() { const { match } = this.props; - this.props.listShortUrls(match.params.serverId, { page: match.params.page }); + console.log(this.props.shortUrlsListParams, match.params, { + ...this.props.shortUrlsListParams, + page: match.params.page + }); + this.props.listShortUrls(match.params.serverId, { + ...this.props.shortUrlsListParams, + page: match.params.page + }); } render() { @@ -129,4 +136,5 @@ class RowMenu extends React.Component { export default connect(state => ({ shortUrlsList: state.shortUrlsList, selectedServer: state.selectedServer, + shortUrlsListParams: state.shortUrlsListParams, }), { listShortUrls })(ShortUrlsList); diff --git a/src/short-urls/reducers/shortUrlsList.js b/src/short-urls/reducers/shortUrlsList.js index 4e218192..7a23e60a 100644 --- a/src/short-urls/reducers/shortUrlsList.js +++ b/src/short-urls/reducers/shortUrlsList.js @@ -1,10 +1,11 @@ -import { LIST_SHORT_URLS } from '../../reducers/types'; +import { LIST_SHORT_URLS, UPDATE_SHORT_URLS_LIST } from '../../reducers/types'; import ServersService from '../../servers/services'; import ShlinkApiClient from '../../api/ShlinkApiClient'; export default function shortUrlsListReducer(state = [], action) { switch (action.type) { case LIST_SHORT_URLS: + case UPDATE_SHORT_URLS_LIST: return action.shortUrls; default: return state; @@ -20,3 +21,10 @@ export const listShortUrls = (serverId, params = {}) => { dispatch({ type: LIST_SHORT_URLS, shortUrls, selectedServer }); }; }; + +export const updateShortUrlsList = (params = {}) => { + return async dispatch => { + const shortUrls = await ShlinkApiClient.listShortUrls(params); + dispatch({ type: UPDATE_SHORT_URLS_LIST, shortUrls, params }); + }; +}; diff --git a/src/short-urls/reducers/shortUrlsListParams.js b/src/short-urls/reducers/shortUrlsListParams.js new file mode 100644 index 00000000..923e4396 --- /dev/null +++ b/src/short-urls/reducers/shortUrlsListParams.js @@ -0,0 +1,10 @@ +import { UPDATE_SHORT_URLS_LIST } from '../../reducers/types'; + +export default function shortUrlsListReducer(state = { page: 1 }, action) { + switch (action.type) { + case UPDATE_SHORT_URLS_LIST: + return { ...state, ...action.params }; + default: + return state; + } +}