diff --git a/src/mercure/helpers/boundToMercureHub.tsx b/src/mercure/helpers/boundToMercureHub.tsx new file mode 100644 index 00000000..e4f7ae71 --- /dev/null +++ b/src/mercure/helpers/boundToMercureHub.tsx @@ -0,0 +1,26 @@ +import React, { FC, useEffect } from 'react'; +import { CreateVisit } from '../../visits/types'; +import { MercureInfo } from '../reducers/mercureInfo'; +import { bindToMercureTopic } from './index'; + +export interface MercureBoundProps { + createNewVisit: (visitData: CreateVisit) => void; + loadMercureInfo: Function; + mercureInfo: MercureInfo; +} + +export function boundToMercureHub( + WrappedComponent: FC, + getTopicForProps: (props: T) => string, +) { + return (props: MercureBoundProps & T) => { + const { createNewVisit, loadMercureInfo, mercureInfo } = props; + + useEffect( + bindToMercureTopic(mercureInfo, getTopicForProps(props), createNewVisit, loadMercureInfo), + [ mercureInfo ], + ); + + return ; + }; +} diff --git a/src/mercure/helpers/index.ts b/src/mercure/helpers/index.ts index 4fa89f24..997ecf07 100644 --- a/src/mercure/helpers/index.ts +++ b/src/mercure/helpers/index.ts @@ -1,4 +1,3 @@ -import { useEffect } from 'react'; import { EventSourcePolyfill as EventSource } from 'event-source-polyfill'; import { MercureInfo } from '../reducers/mercureInfo'; @@ -23,18 +22,3 @@ export const bindToMercureTopic = (mercureInfo: MercureInfo, topic: string, o return () => es.close(); }; - -export const useMercureTopicBinding = ( - mercureInfo: MercureInfo, - topic: string, - onMessage: (message: T) => void, - onTokenExpired: Function, -) => { - useEffect(bindToMercureTopic(mercureInfo, topic, onMessage, onTokenExpired), [ mercureInfo ]); -}; - -export interface MercureBoundProps { - createNewVisit: (message: any) => void; - loadMercureInfo: Function; - mercureInfo: MercureInfo; -} diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx index ccab7bca..6cb2c947 100644 --- a/src/short-urls/ShortUrlsList.tsx +++ b/src/short-urls/ShortUrlsList.tsx @@ -6,8 +6,8 @@ import qs from 'qs'; import { RouteComponentProps } from 'react-router'; import SortingDropdown from '../utils/SortingDropdown'; import { determineOrderDir, OrderDir } from '../utils/utils'; -import { MercureBoundProps, useMercureTopicBinding } from '../mercure/helpers'; import { SelectedServer } from '../servers/data'; +import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub'; import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList'; import { ShortUrlsRowProps } from './helpers/ShortUrlsRow'; import { ShortUrl } from './data'; @@ -31,14 +31,14 @@ export interface WithList { shortUrlsList: ShortUrl[]; } -export interface ShortUrlsListProps extends ShortUrlsListState, RouteComponentProps, MercureBoundProps { +export interface ShortUrlsListProps extends ShortUrlsListState, RouteComponentProps { selectedServer: SelectedServer; listShortUrls: (params: ShortUrlsListParams) => void; shortUrlsListParams: ShortUrlsListParams; resetShortUrlParams: () => void; } -const ShortUrlsList = (ShortUrlsRow: FC) => ({ +const ShortUrlsList = (ShortUrlsRow: FC) => boundToMercureHub(({ listShortUrls, resetShortUrlParams, shortUrlsListParams, @@ -48,9 +48,6 @@ const ShortUrlsList = (ShortUrlsRow: FC) => ({ error, shortUrlsList, selectedServer, - createNewVisit, - loadMercureInfo, - mercureInfo, }: ShortUrlsListProps & WithList) => { const { orderBy } = shortUrlsListParams; const [ order, setOrder ] = useState<{ orderField?: OrderableFields; orderDir?: OrderDir }>({ @@ -116,7 +113,6 @@ const ShortUrlsList = (ShortUrlsRow: FC) => ({ return resetShortUrlParams; }, []); - useMercureTopicBinding(mercureInfo, 'https://shlink.io/new-visit', createNewVisit, loadMercureInfo); return ( @@ -168,6 +164,6 @@ const ShortUrlsList = (ShortUrlsRow: FC) => ({ ); -}; +}, () => 'https://shlink.io/new-visit'); export default ShortUrlsList; diff --git a/src/tags/TagsList.tsx b/src/tags/TagsList.tsx index e73ee4da..e9dd8bb6 100644 --- a/src/tags/TagsList.tsx +++ b/src/tags/TagsList.tsx @@ -2,30 +2,29 @@ import React, { FC, useEffect, useState } from 'react'; import { splitEvery } from 'ramda'; import Message from '../utils/Message'; import SearchField from '../utils/SearchField'; -import { MercureBoundProps, useMercureTopicBinding } from '../mercure/helpers'; import { SelectedServer } from '../servers/data'; +import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub'; import { TagsList as TagsListState } from './reducers/tagsList'; import { TagCardProps } from './TagCard'; const { ceil } = Math; const TAGS_GROUPS_AMOUNT = 4; -export interface TagsListProps extends MercureBoundProps { +export interface TagsListProps { filterTags: (searchTerm: string) => void; forceListTags: Function; tagsList: TagsListState; selectedServer: SelectedServer; } -const TagsList = (TagCard: FC) => ( - { filterTags, forceListTags, tagsList, selectedServer, createNewVisit, loadMercureInfo, mercureInfo }: TagsListProps, +const TagsList = (TagCard: FC) => boundToMercureHub(( + { filterTags, forceListTags, tagsList, selectedServer }: TagsListProps, ) => { const [ displayedTag, setDisplayedTag ] = useState(); useEffect(() => { forceListTags(); }, []); - useMercureTopicBinding(mercureInfo, 'https://shlink.io/new-visit', createNewVisit, loadMercureInfo); const renderContent = () => { if (tagsList.loading) { @@ -76,6 +75,6 @@ const TagsList = (TagCard: FC) => ( ); -}; +}, () => 'https://shlink.io/new-visit'); export default TagsList; diff --git a/src/visits/ShortUrlVisits.tsx b/src/visits/ShortUrlVisits.tsx index 2d5f9461..7d6f6e8f 100644 --- a/src/visits/ShortUrlVisits.tsx +++ b/src/visits/ShortUrlVisits.tsx @@ -1,14 +1,14 @@ import React, { useEffect } from 'react'; import qs from 'qs'; import { RouteComponentProps } from 'react-router'; -import { MercureBoundProps, useMercureTopicBinding } from '../mercure/helpers'; +import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub'; import { ShlinkVisitsParams } from '../utils/services/types'; import { ShortUrlVisits as ShortUrlVisitsState } from './reducers/shortUrlVisits'; import ShortUrlVisitsHeader from './ShortUrlVisitsHeader'; import { ShortUrlDetail } from './reducers/shortUrlDetail'; import VisitsStats from './VisitsStats'; -export interface ShortUrlVisitsProps extends RouteComponentProps<{ shortCode: string }>, MercureBoundProps { +export interface ShortUrlVisitsProps extends RouteComponentProps<{ shortCode: string }> { getShortUrlVisits: (shortCode: string, query?: ShlinkVisitsParams) => void; shortUrlVisits: ShortUrlVisitsState; getShortUrlDetail: Function; @@ -16,7 +16,7 @@ export interface ShortUrlVisitsProps extends RouteComponentProps<{ shortCode: st cancelGetShortUrlVisits: () => void; } -const ShortUrlVisits = ({ +const ShortUrlVisits = boundToMercureHub(({ history: { goBack }, match, location: { search }, @@ -25,9 +25,6 @@ const ShortUrlVisits = ({ getShortUrlVisits, getShortUrlDetail, cancelGetShortUrlVisits, - createNewVisit, - loadMercureInfo, - mercureInfo, }: ShortUrlVisitsProps) => { const { params } = match; const { shortCode } = params; @@ -38,13 +35,12 @@ const ShortUrlVisits = ({ useEffect(() => { getShortUrlDetail(shortCode, domain); }, []); - useMercureTopicBinding(mercureInfo, `https://shlink.io/new-visit/${shortCode}`, createNewVisit, loadMercureInfo); return ( ); -}; +}, ({ match }) => `https://shlink.io/new-visit/${match.params.shortCode}`); export default ShortUrlVisits; diff --git a/src/visits/TagVisits.tsx b/src/visits/TagVisits.tsx index b14c3085..877bcbee 100644 --- a/src/visits/TagVisits.tsx +++ b/src/visits/TagVisits.tsx @@ -1,39 +1,34 @@ import React from 'react'; import { RouteComponentProps } from 'react-router'; -import { MercureBoundProps, useMercureTopicBinding } from '../mercure/helpers'; +import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub'; import ColorGenerator from '../utils/services/ColorGenerator'; import { ShlinkVisitsParams } from '../utils/services/types'; import { TagVisits as TagVisitsState } from './reducers/tagVisits'; import TagVisitsHeader from './TagVisitsHeader'; import VisitsStats from './VisitsStats'; -export interface TagVisitsProps extends RouteComponentProps<{ tag: string }>, MercureBoundProps { +export interface TagVisitsProps extends RouteComponentProps<{ tag: string }> { getTagVisits: (tag: string, query: any) => void; tagVisits: TagVisitsState; cancelGetTagVisits: () => void; } -const TagVisits = (colorGenerator: ColorGenerator) => ({ +const TagVisits = (colorGenerator: ColorGenerator) => boundToMercureHub(({ history: { goBack }, match, getTagVisits, tagVisits, cancelGetTagVisits, - createNewVisit, - loadMercureInfo, - mercureInfo, }: TagVisitsProps) => { const { params } = match; const { tag } = params; const loadVisits = (params: ShlinkVisitsParams) => getTagVisits(tag, params); - useMercureTopicBinding(mercureInfo, 'https://shlink.io/new-visit', createNewVisit, loadMercureInfo); - return ( ); -}; +}, () => 'https://shlink.io/new-visit'); export default TagVisits; diff --git a/test/App.test.tsx b/test/App.test.tsx index fe50f068..9b8d4070 100644 --- a/test/App.test.tsx +++ b/test/App.test.tsx @@ -7,10 +7,9 @@ import appFactory from '../src/App'; describe('', () => { let wrapper: ShallowWrapper; const MainHeader = () => null; - const DummyComponent = () => null; beforeEach(() => { - const App = appFactory(MainHeader, DummyComponent, DummyComponent, DummyComponent, DummyComponent, DummyComponent); + const App = appFactory(MainHeader, () => null, () => null, () => null, () => null, () => null, () => null); wrapper = shallow(); }); diff --git a/test/short-urls/ShortUrlsList.test.tsx b/test/short-urls/ShortUrlsList.test.tsx index 60161780..9895d058 100644 --- a/test/short-urls/ShortUrlsList.test.tsx +++ b/test/short-urls/ShortUrlsList.test.tsx @@ -5,6 +5,7 @@ import { faCaretDown as caretDownIcon, faCaretUp as caretUpIcon } from '@fortawe import { Mock } from 'ts-mockery'; import shortUrlsListCreator, { ShortUrlsListProps, SORTABLE_FIELDS } from '../../src/short-urls/ShortUrlsList'; import { ShortUrl } from '../../src/short-urls/data'; +import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; describe('', () => { let wrapper: ShallowWrapper; @@ -18,6 +19,7 @@ describe('', () => { wrapper = shallow( ()} + {...Mock.of({ mercureInfo: { loading: true } })} listShortUrls={listShortUrlsMock} resetShortUrlParams={resetShortUrlParamsMock} shortUrlsListParams={{ @@ -39,9 +41,8 @@ describe('', () => { }), ] } - mercureInfo={{ loading: true } as any} />, - ); + ).dive(); // Dive is needed as this component is wrapped in a HOC }); afterEach(jest.resetAllMocks); diff --git a/test/tags/TagsList.test.tsx b/test/tags/TagsList.test.tsx index 4cfe8790..b72f5dc1 100644 --- a/test/tags/TagsList.test.tsx +++ b/test/tags/TagsList.test.tsx @@ -7,6 +7,7 @@ import Message from '../../src/utils/Message'; import SearchField from '../../src/utils/SearchField'; import { rangeOf } from '../../src/utils/utils'; import { TagsList } from '../../src/tags/reducers/tagsList'; +import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; describe('', () => { let wrapper: ShallowWrapper; @@ -18,11 +19,12 @@ describe('', () => { wrapper = shallow( ()} + {...Mock.all()} forceListTags={identity} filterTags={filterTags} tagsList={Mock.of(tagsList)} />, - ); + ).dive(); // Dive is needed as this component is wrapped in a HOC return wrapper; }; diff --git a/test/visits/ShortUrlVisits.test.tsx b/test/visits/ShortUrlVisits.test.tsx index f3516b4e..8a980daa 100644 --- a/test/visits/ShortUrlVisits.test.tsx +++ b/test/visits/ShortUrlVisits.test.tsx @@ -9,6 +9,7 @@ import ShortUrlVisitsHeader from '../../src/visits/ShortUrlVisitsHeader'; import { ShortUrlVisits as ShortUrlVisitsState } from '../../src/visits/reducers/shortUrlVisits'; import { ShortUrlDetail } from '../../src/visits/reducers/shortUrlDetail'; import VisitsStats from '../../src/visits/VisitsStats'; +import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; describe('', () => { let wrapper: ShallowWrapper; @@ -25,6 +26,7 @@ describe('', () => { wrapper = shallow( ()} + {...Mock.all()} getShortUrlDetail={identity} getShortUrlVisits={getShortUrlVisitsMock} match={match} @@ -34,7 +36,7 @@ describe('', () => { shortUrlDetail={Mock.all()} cancelGetShortUrlVisits={() => {}} />, - ); + ).dive(); // Dive is needed as this component is wrapped in a HOC }); afterEach(() => wrapper.unmount()); diff --git a/test/visits/TagVisits.test.tsx b/test/visits/TagVisits.test.tsx index 6c8e8b1c..5cc80f03 100644 --- a/test/visits/TagVisits.test.tsx +++ b/test/visits/TagVisits.test.tsx @@ -8,6 +8,7 @@ import TagVisitsHeader from '../../src/visits/TagVisitsHeader'; import ColorGenerator from '../../src/utils/services/ColorGenerator'; import { TagVisits as TagVisitsStats } from '../../src/visits/reducers/tagVisits'; import VisitsStats from '../../src/visits/VisitsStats'; +import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; describe('', () => { let wrapper: ShallowWrapper; @@ -25,13 +26,14 @@ describe('', () => { wrapper = shallow( ()} + {...Mock.all()} getTagVisits={getTagVisitsMock} match={match} history={history} tagVisits={Mock.of({ loading: true, visits: [] })} cancelGetTagVisits={() => {}} />, - ); + ).dive(); // Dive is needed as this component is wrapped in a HOC }); afterEach(() => wrapper.unmount());