Added EventSource connection to mercure hub possible

This commit is contained in:
Alejandro Celaya
2020-04-17 17:11:52 +02:00
parent 37e6c27461
commit f3129399de
9 changed files with 86 additions and 46 deletions

View File

@@ -17,7 +17,7 @@ export const MercureInfoType = PropTypes.shape({
const initialState = {
token: undefined,
mercureHubUrl: undefined,
loading: false,
loading: true,
error: false,
};

View File

@@ -4,9 +4,11 @@ import { head, isEmpty, keys, values } from 'ramda';
import React from 'react';
import qs from 'qs';
import PropTypes from 'prop-types';
import { EventSourcePolyfill as EventSource } from 'event-source-polyfill';
import { serverType } from '../servers/prop-types';
import SortingDropdown from '../utils/SortingDropdown';
import { determineOrderDir } from '../utils/utils';
import { MercureInfoType } from '../mercure/reducers/mercureInfo';
import { shortUrlType } from './reducers/shortUrlsList';
import { shortUrlsListParamsType } from './reducers/shortUrlsListParams';
import './ShortUrlsList.scss';
@@ -30,6 +32,8 @@ const ShortUrlsList = (ShortUrlsRow) => class ShortUrlsList extends React.Compon
error: PropTypes.bool,
shortUrlsList: PropTypes.arrayOf(shortUrlType),
selectedServer: serverType,
createNewVisit: PropTypes.func,
mercureInfo: MercureInfoType,
};
refreshList = (extraParams) => {
@@ -85,12 +89,40 @@ const ShortUrlsList = (ShortUrlsRow) => class ShortUrlsList extends React.Compon
this.refreshList({ page: params.page, tags });
}
componentDidUpdate() {
const { mercureHubUrl, token, loading, error } = this.props.mercureInfo;
if (loading || error) {
return;
}
const hubUrl = new URL(mercureHubUrl);
hubUrl.searchParams.append('topic', 'https://shlink.io/new-visit');
this.closeEventSource();
this.es = new EventSource(hubUrl, {
headers: {
Authorization: `Bearer ${token}`,
},
});
this.es.onmessage = ({ data }) => this.props.createNewVisit(JSON.parse(data));
}
componentWillUnmount() {
const { resetShortUrlParams } = this.props;
this.closeEventSource();
resetShortUrlParams();
}
closeEventSource = () => {
if (this.es) {
this.es.close();
this.es = undefined;
}
}
renderShortUrls() {
const { shortUrlsList, selectedServer, loading, error, shortUrlsListParams } = this.props;

View File

@@ -1,6 +1,7 @@
import { handleActions } from 'redux-actions';
import { assoc, assocPath, isNil, reject } from 'ramda';
import PropTypes from 'prop-types';
import { CREATE_SHORT_URL_VISIT } from '../../visits/reducers/shortUrlVisits';
import { SHORT_URL_TAGS_EDITED } from './shortUrlTags';
import { SHORT_URL_DELETED } from './shortUrlDeletion';
import { SHORT_URL_META_EDITED, shortUrlMetaType } from './shortUrlMeta';
@@ -56,6 +57,15 @@ export default handleActions({
[SHORT_URL_TAGS_EDITED]: setPropFromActionOnMatchingShortUrl('tags'),
[SHORT_URL_META_EDITED]: setPropFromActionOnMatchingShortUrl('meta'),
[SHORT_URL_EDITED]: setPropFromActionOnMatchingShortUrl('longUrl'),
[CREATE_SHORT_URL_VISIT]: (state, { shortUrl: { shortCode, domain, visitsCount } }) => assocPath(
[ 'shortUrls', 'data' ],
state.shortUrls.data.map(
(shortUrl) => shortUrlMatches(shortUrl, shortCode, domain)
? assoc('visitsCount', visitsCount, shortUrl)
: shortUrl
),
state
),
}, initialState);
export const listShortUrls = (buildShlinkApiClient) => (params = {}) => async (dispatch, getState) => {

View File

@@ -31,8 +31,8 @@ const provideServices = (bottle, connect) => {
bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsRow');
bottle.decorator('ShortUrlsList', connect(
[ 'selectedServer', 'shortUrlsListParams' ],
[ 'listShortUrls', 'resetShortUrlParams' ]
[ 'selectedServer', 'shortUrlsListParams', 'mercureInfo' ],
[ 'listShortUrls', 'resetShortUrlParams', 'createNewVisit' ]
));
bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator', 'useStateFlagTimeout');

View File

@@ -8,6 +8,7 @@ export const GET_SHORT_URL_VISITS_ERROR = 'shlink/shortUrlVisits/GET_SHORT_URL_V
export const GET_SHORT_URL_VISITS = 'shlink/shortUrlVisits/GET_SHORT_URL_VISITS';
export const GET_SHORT_URL_VISITS_LARGE = 'shlink/shortUrlVisits/GET_SHORT_URL_VISITS_LARGE';
export const GET_SHORT_URL_VISITS_CANCEL = 'shlink/shortUrlVisits/GET_SHORT_URL_VISITS_CANCEL';
export const CREATE_SHORT_URL_VISIT = 'shlink/shortUrlVisits/CREATE_SHORT_URL_VISIT';
/* eslint-enable padding-line-between-statements */
export const visitType = PropTypes.shape({
@@ -63,6 +64,9 @@ export default handleActions({
}),
[GET_SHORT_URL_VISITS_LARGE]: (state) => ({ ...state, loadingLarge: true }),
[GET_SHORT_URL_VISITS_CANCEL]: (state) => ({ ...state, cancelLoad: true }),
// TODO
[CREATE_SHORT_URL_VISIT]: (state) => state,
}, initialState);
export const getShortUrlVisits = (buildShlinkApiClient) => (shortCode, query) => async (dispatch, getState) => {
@@ -125,3 +129,5 @@ export const getShortUrlVisits = (buildShlinkApiClient) => (shortCode, query) =>
};
export const cancelGetShortUrlVisits = createAction(GET_SHORT_URL_VISITS_CANCEL);
export const createNewVisit = ({ shortUrl, visit }) => ({ shortUrl, visit, type: CREATE_SHORT_URL_VISIT });

View File

@@ -1,5 +1,5 @@
import ShortUrlVisits from '../ShortUrlVisits';
import { cancelGetShortUrlVisits, getShortUrlVisits } from '../reducers/shortUrlVisits';
import { cancelGetShortUrlVisits, createNewVisit, getShortUrlVisits } from '../reducers/shortUrlVisits';
import { getShortUrlDetail } from '../reducers/shortUrlDetail';
import OpenMapModalBtn from '../helpers/OpenMapModalBtn';
import MapModal from '../helpers/MapModal';
@@ -22,6 +22,7 @@ const provideServices = (bottle, connect) => {
bottle.serviceFactory('getShortUrlVisits', getShortUrlVisits, 'buildShlinkApiClient');
bottle.serviceFactory('getShortUrlDetail', getShortUrlDetail, 'buildShlinkApiClient');
bottle.serviceFactory('cancelGetShortUrlVisits', () => cancelGetShortUrlVisits);
bottle.serviceFactory('createNewVisit', () => createNewVisit);
};
export default provideServices;