From 2ac5236cc7fb735da8b94a0ee51a6b219d6bc0e2 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 24 Jul 2023 09:48:41 +0200 Subject: [PATCH] Provide API client to shlink-web-component --- src/common/MenuLayout.tsx | 3 + src/common/services/provideServices.ts | 2 +- src/shlink-web-component/Main.tsx | 75 ++++++++++++++++ .../ShlinkWebComponent.tsx | 90 +++++-------------- src/shlink-web-component/container/index.ts | 6 +- .../container/provideServices.ts | 7 +- .../domains/reducers/domainRedirects.ts | 10 +-- .../domains/reducers/domainsList.ts | 24 +++-- .../domains/services/provideServices.ts | 4 +- src/shlink-web-component/index.ts | 6 +- .../mercure/reducers/mercureInfo.ts | 10 +-- .../mercure/services/provideServices.ts | 2 +- .../short-urls/helpers/ExportShortUrlsBtn.tsx | 7 +- .../short-urls/reducers/shortUrlCreation.ts | 6 +- .../short-urls/reducers/shortUrlDeletion.ts | 9 +- .../short-urls/reducers/shortUrlDetail.ts | 6 +- .../short-urls/reducers/shortUrlEdition.ts | 11 ++- .../short-urls/reducers/shortUrlsList.ts | 15 ++-- .../short-urls/services/provideServices.ts | 12 +-- .../tags/reducers/tagDelete.ts | 9 +- .../tags/reducers/tagEdit.ts | 8 +- .../tags/reducers/tagsList.ts | 9 +- .../tags/services/provideServices.ts | 7 +- .../visits/reducers/common.ts | 4 +- .../visits/reducers/domainVisits.ts | 8 +- .../visits/reducers/nonOrphanVisits.ts | 7 +- .../visits/reducers/orphanVisits.ts | 7 +- .../visits/reducers/shortUrlVisits.ts | 8 +- .../visits/reducers/tagVisits.ts | 8 +- .../visits/reducers/visitsOverview.ts | 20 ++--- .../visits/services/provideServices.ts | 12 +-- 31 files changed, 220 insertions(+), 192 deletions(-) create mode 100644 src/shlink-web-component/Main.tsx diff --git a/src/common/MenuLayout.tsx b/src/common/MenuLayout.tsx index 3ba1d654..42149b6f 100644 --- a/src/common/MenuLayout.tsx +++ b/src/common/MenuLayout.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react'; import { useEffect } from 'react'; +import type { ShlinkApiClientBuilder } from '../api/services/ShlinkApiClientBuilder'; import { isReachableServer } from '../servers/data'; import { withSelectedServer } from '../servers/helpers/withSelectedServer'; import { ShlinkWebComponent } from '../shlink-web-component'; @@ -13,6 +14,7 @@ interface MenuLayoutProps { } export const MenuLayout = ( + buildShlinkApiClient: ShlinkApiClientBuilder, ServerError: FC, ) => withSelectedServer(({ selectedServer, sidebarNotPresent, sidebarPresent, settings }) => { const showContent = isReachableServer(selectedServer); @@ -29,6 +31,7 @@ export const MenuLayout = ( return ( diff --git a/src/common/services/provideServices.ts b/src/common/services/provideServices.ts index f5811e8d..57e91e10 100644 --- a/src/common/services/provideServices.ts +++ b/src/common/services/provideServices.ts @@ -31,7 +31,7 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.decorator('Home', withoutSelectedServer); bottle.decorator('Home', connect(['servers'], ['resetSelectedServer'])); - bottle.serviceFactory('MenuLayout', MenuLayout, 'ServerError'); + bottle.serviceFactory('MenuLayout', MenuLayout, 'buildShlinkApiClient', 'ServerError'); bottle.decorator('MenuLayout', connect( ['selectedServer', 'settings'], ['selectServer', 'sidebarPresent', 'sidebarNotPresent'], diff --git a/src/shlink-web-component/Main.tsx b/src/shlink-web-component/Main.tsx new file mode 100644 index 00000000..a15980ed --- /dev/null +++ b/src/shlink-web-component/Main.tsx @@ -0,0 +1,75 @@ +import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import classNames from 'classnames'; +import type { FC } from 'react'; +import { useEffect } from 'react'; +import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; +import { AsideMenu } from '../common/AsideMenu'; +import { NotFound } from '../common/NotFound'; +import { useSwipeable, useToggle } from '../utils/helpers/hooks'; +import { useFeature } from './utils/features'; + +type MainProps = { + routesPrefix?: string; +}; + +export const Main = ( + TagsList: FC, + ShortUrlsList: FC, + CreateShortUrl: FC, + ShortUrlVisits: FC, + TagVisits: FC, + DomainVisits: FC, + OrphanVisits: FC, + NonOrphanVisits: FC, + Overview: FC, + EditShortUrl: FC, + ManageDomains: FC, +): FC => ({ routesPrefix = '' }) => { + const location = useLocation(); + const [sidebarVisible, toggleSidebar, showSidebar, hideSidebar] = useToggle(); + useEffect(() => hideSidebar(), [location]); + + const addNonOrphanVisitsRoute = useFeature('nonOrphanVisits'); + const addDomainVisitsRoute = useFeature('domainVisits'); + const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible }); + const swipeableProps = useSwipeable(showSidebar, hideSidebar); + + // TODO Check if this is already wrapped by a router, and wrap otherwise + + return ( + <> + + +
+
+ +
hideSidebar()}> +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + {addDomainVisitsRoute && } />} + } /> + {addNonOrphanVisitsRoute && } />} + } /> + } /> + List short URLs} + /> + +
+
+
+
+ + ); +}; + +export type MainType = ReturnType; diff --git a/src/shlink-web-component/ShlinkWebComponent.tsx b/src/shlink-web-component/ShlinkWebComponent.tsx index d248accc..778ea8ba 100644 --- a/src/shlink-web-component/ShlinkWebComponent.tsx +++ b/src/shlink-web-component/ShlinkWebComponent.tsx @@ -1,88 +1,46 @@ -import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import type { Store } from '@reduxjs/toolkit'; -import classNames from 'classnames'; -import type { FC } from 'react'; -import { useEffect } from 'react'; +import type Bottle from 'bottlejs'; +import type { FC, ReactNode } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { Provider } from 'react-redux'; -import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; -import { AsideMenu } from '../common/AsideMenu'; -import { NotFound } from '../common/NotFound'; -import { useSwipeable, useToggle } from '../utils/helpers/hooks'; import type { SemVer } from '../utils/helpers/version'; +import { setUpStore } from './container/store'; import { FeaturesProvider, useFeatures } from './utils/features'; import type { Settings } from './utils/settings'; import { SettingsProvider } from './utils/settings'; type ShlinkWebComponentProps = { routesPrefix?: string; - serverVersion: SemVer; settings?: Settings; + serverVersion: SemVer; + apiClient: any; }; -export const ShlinkWebComponent = ( - TagsList: FC, - ShortUrlsList: FC, - CreateShortUrl: FC, - ShortUrlVisits: FC, - TagVisits: FC, - DomainVisits: FC, - OrphanVisits: FC, - NonOrphanVisits: FC, - Overview: FC, - EditShortUrl: FC, - ManageDomains: FC, - store: Store, -): FC => ({ routesPrefix = '', serverVersion, settings }) => { - const location = useLocation(); - const [sidebarVisible, toggleSidebar, showSidebar, hideSidebar] = useToggle(); - useEffect(() => hideSidebar(), [location]); - +export const createShlinkWebComponent = ( + bottle: Bottle, +): FC => ({ routesPrefix = '', serverVersion, settings, apiClient }) => { const features = useFeatures(serverVersion); - const addNonOrphanVisitsRoute = features.nonOrphanVisits; - const addDomainVisitsRoute = features.domainVisits; - const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible }); - const swipeableProps = useSwipeable(showSidebar, hideSidebar); + const mainContent = useRef(); + const [theStore, setStore] = useState(); - // TODO Check if this is already wrapped by a router, and wrap otherwise + useEffect(() => { + bottle.constant('apiClient', apiClient); - return ( - + // It's important to not try to resolve services before the API client has been registered, as many other services + // depend on it + const { container } = bottle; + const { Main } = container; + mainContent.current =
; + setStore(setUpStore(container)); + }, []); + + return !theStore ? <> : ( + - - -
-
- -
hideSidebar()}> -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - {addDomainVisitsRoute && } />} - } /> - {addNonOrphanVisitsRoute && } />} - } /> - } /> - List short URLs} - /> - -
-
-
-
+ {mainContent.current}
); }; - -export type ShlinkWebComponentType = ReturnType; diff --git a/src/shlink-web-component/container/index.ts b/src/shlink-web-component/container/index.ts index 0d0cf953..672b5e07 100644 --- a/src/shlink-web-component/container/index.ts +++ b/src/shlink-web-component/container/index.ts @@ -16,13 +16,12 @@ import { provideServices as provideShortUrlsServices } from '../short-urls/servi import { provideServices as provideTagsServices } from '../tags/services/provideServices'; import { provideServices as provideVisitsServices } from '../visits/services/provideServices'; import { provideServices as provideWebComponentServices } from './provideServices'; -import { setUpStore } from './store'; type LazyActionMap = Record; export type ConnectDecorator = (props: string[] | null, actions?: string[]) => any; -const bottle = new Bottle(); +export const bottle = new Bottle(); export const { container } = bottle; @@ -66,6 +65,3 @@ bottle.constant('jsonToCsv', jsonToCsv); bottle.constant('setTimeout', window.setTimeout); bottle.constant('clearTimeout', window.clearTimeout); bottle.serviceFactory('useTimeoutToggle', useTimeoutToggle, 'setTimeout', 'clearTimeout'); - -// FIXME This has to be last. Find a way to delay the creation, perhaps using some kind of runtime factory -bottle.constant('store', setUpStore(container)); diff --git a/src/shlink-web-component/container/provideServices.ts b/src/shlink-web-component/container/provideServices.ts index 0e54b9c8..8368c237 100644 --- a/src/shlink-web-component/container/provideServices.ts +++ b/src/shlink-web-component/container/provideServices.ts @@ -1,10 +1,10 @@ import type Bottle from 'bottlejs'; -import { ShlinkWebComponent } from '../ShlinkWebComponent'; +import { Main } from '../Main'; export const provideServices = (bottle: Bottle) => { bottle.serviceFactory( - 'ShlinkWebComponent', - ShlinkWebComponent, + 'Main', + Main, 'TagsList', 'ShortUrlsList', 'CreateShortUrl', @@ -16,6 +16,5 @@ export const provideServices = (bottle: Bottle) => { 'Overview', 'EditShortUrl', 'ManageDomains', - 'store', ); }; diff --git a/src/shlink-web-component/domains/reducers/domainRedirects.ts b/src/shlink-web-component/domains/reducers/domainRedirects.ts index 582a5adc..c1db9f6c 100644 --- a/src/shlink-web-component/domains/reducers/domainRedirects.ts +++ b/src/shlink-web-component/domains/reducers/domainRedirects.ts @@ -1,4 +1,4 @@ -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ShlinkDomainRedirects } from '../../../api/types'; import { createAsyncThunk } from '../../../utils/helpers/redux'; @@ -10,13 +10,11 @@ export interface EditDomainRedirects { } export const editDomainRedirects = ( - buildShlinkApiClient: ShlinkApiClientBuilder, + apiClient: ShlinkApiClient, ) => createAsyncThunk( EDIT_DOMAIN_REDIRECTS, - async ({ domain, redirects: providedRedirects }: EditDomainRedirects, { getState }): Promise => { - const { editDomainRedirects: shlinkEditDomainRedirects } = buildShlinkApiClient(getState); - const redirects = await shlinkEditDomainRedirects({ domain, ...providedRedirects }); - + async ({ domain, redirects: providedRedirects }: EditDomainRedirects): Promise => { + const redirects = await apiClient.editDomainRedirects({ domain, ...providedRedirects }); return { domain, redirects }; }, ); diff --git a/src/shlink-web-component/domains/reducers/domainsList.ts b/src/shlink-web-component/domains/reducers/domainsList.ts index 58d84edf..6f38d09b 100644 --- a/src/shlink-web-component/domains/reducers/domainsList.ts +++ b/src/shlink-web-component/domains/reducers/domainsList.ts @@ -1,12 +1,11 @@ import type { AsyncThunk, SliceCaseReducers } from '@reduxjs/toolkit'; import { createAction, createSlice } from '@reduxjs/toolkit'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ShlinkDomainRedirects } from '../../../api/types'; import type { ProblemDetailsError } from '../../../api/types/errors'; import { parseApiError } from '../../../api/utils'; import { hasServerData } from '../../../servers/data'; import { createAsyncThunk } from '../../../utils/helpers/redux'; -import { replaceAuthorityFromUri } from '../../../utils/helpers/uri'; import type { Domain, DomainStatus } from '../data'; import type { EditDomainRedirects } from './domainRedirects'; @@ -45,12 +44,11 @@ export const replaceStatusOnDomain = (domain: string, status: DomainStatus) => (d: Domain): Domain => (d.domain !== domain ? d : { ...d, status }); export const domainsListReducerCreator = ( - buildShlinkApiClient: ShlinkApiClientBuilder, + apiClient: ShlinkApiClient, editDomainRedirects: AsyncThunk, ) => { - const listDomains = createAsyncThunk(`${REDUCER_PREFIX}/listDomains`, async (_: void, { getState }): Promise => { - const { listDomains: shlinkListDomains } = buildShlinkApiClient(getState); - const { data, defaultRedirects } = await shlinkListDomains(); + const listDomains = createAsyncThunk(`${REDUCER_PREFIX}/listDomains`, async (): Promise => { + const { data, defaultRedirects } = await apiClient.listDomains(); return { domains: data.map((domain): Domain => ({ ...domain, status: 'validating' })), @@ -68,13 +66,13 @@ export const domainsListReducerCreator = ( } try { - const { url, ...rest } = selectedServer; - const { health } = buildShlinkApiClient({ - ...rest, - url: replaceAuthorityFromUri(url, domain), - }); - - const { status } = await health(); + // FIXME This should call different domains + // const { url, ...rest } = selectedServer; + // const { health } = buildShlinkApiClient({ + // ...rest, + // url: replaceAuthorityFromUri(url, domain), + // }); + const { status } = await apiClient.health(); return { domain, status: status === 'pass' ? 'valid' : 'invalid' }; } catch (e) { diff --git a/src/shlink-web-component/domains/services/provideServices.ts b/src/shlink-web-component/domains/services/provideServices.ts index 30e944d0..a4bb28e6 100644 --- a/src/shlink-web-component/domains/services/provideServices.ts +++ b/src/shlink-web-component/domains/services/provideServices.ts @@ -21,7 +21,7 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory( 'domainsListReducerCreator', domainsListReducerCreator, - 'buildShlinkApiClient', + 'apiClient', 'editDomainRedirects', ); bottle.serviceFactory('domainsListReducer', prop('reducer'), 'domainsListReducerCreator'); @@ -29,6 +29,6 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { // Actions bottle.serviceFactory('listDomains', prop('listDomains'), 'domainsListReducerCreator'); bottle.serviceFactory('filterDomains', prop('filterDomains'), 'domainsListReducerCreator'); - bottle.serviceFactory('editDomainRedirects', editDomainRedirects, 'buildShlinkApiClient'); + bottle.serviceFactory('editDomainRedirects', editDomainRedirects, 'apiClient'); bottle.serviceFactory('checkDomainHealth', prop('checkDomainHealth'), 'domainsListReducerCreator'); }; diff --git a/src/shlink-web-component/index.ts b/src/shlink-web-component/index.ts index f21ab8aa..74fcebee 100644 --- a/src/shlink-web-component/index.ts +++ b/src/shlink-web-component/index.ts @@ -1,4 +1,4 @@ -import { container } from './container'; -import type { ShlinkWebComponentType } from './ShlinkWebComponent'; +import { bottle } from './container'; +import { createShlinkWebComponent } from './ShlinkWebComponent'; -export const ShlinkWebComponent = container.ShlinkWebComponent as ShlinkWebComponentType; +export const ShlinkWebComponent = createShlinkWebComponent(bottle); diff --git a/src/shlink-web-component/mercure/reducers/mercureInfo.ts b/src/shlink-web-component/mercure/reducers/mercureInfo.ts index 806e387b..2ac3f144 100644 --- a/src/shlink-web-component/mercure/reducers/mercureInfo.ts +++ b/src/shlink-web-component/mercure/reducers/mercureInfo.ts @@ -1,5 +1,5 @@ import { createSlice } from '@reduxjs/toolkit'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ShlinkMercureInfo } from '../../../api/types'; import { createAsyncThunk } from '../../../utils/helpers/redux'; @@ -16,17 +16,17 @@ const initialState: MercureInfo = { error: false, }; -export const mercureInfoReducerCreator = (buildShlinkApiClient: ShlinkApiClientBuilder) => { +export const mercureInfoReducerCreator = (apiClient: ShlinkApiClient) => { const loadMercureInfo = createAsyncThunk( `${REDUCER_PREFIX}/loadMercureInfo`, - (_: void, { getState }): Promise => - // TODO Get settings here, where info is only available via hook + (): Promise => + // FIXME Get settings here somehow, as they are only available via hook // const { settings } = getState(); // if (!settings.realTimeUpdates.enabled) { // throw new Error('Real time updates not enabled'); // } - buildShlinkApiClient(getState).mercureInfo() + apiClient.mercureInfo() , ); diff --git a/src/shlink-web-component/mercure/services/provideServices.ts b/src/shlink-web-component/mercure/services/provideServices.ts index 0b6b4121..7dcc952d 100644 --- a/src/shlink-web-component/mercure/services/provideServices.ts +++ b/src/shlink-web-component/mercure/services/provideServices.ts @@ -4,7 +4,7 @@ import { mercureInfoReducerCreator } from '../reducers/mercureInfo'; export const provideServices = (bottle: Bottle) => { // Reducer - bottle.serviceFactory('mercureInfoReducerCreator', mercureInfoReducerCreator, 'buildShlinkApiClient'); + bottle.serviceFactory('mercureInfoReducerCreator', mercureInfoReducerCreator, 'apiClient'); bottle.serviceFactory('mercureInfoReducer', prop('reducer'), 'mercureInfoReducerCreator'); // Actions diff --git a/src/shlink-web-component/short-urls/helpers/ExportShortUrlsBtn.tsx b/src/shlink-web-component/short-urls/helpers/ExportShortUrlsBtn.tsx index e07bb021..0ba5839a 100644 --- a/src/shlink-web-component/short-urls/helpers/ExportShortUrlsBtn.tsx +++ b/src/shlink-web-component/short-urls/helpers/ExportShortUrlsBtn.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react'; import { useCallback } from 'react'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ReportExporter } from '../../../common/services/ReportExporter'; import type { SelectedServer } from '../../../servers/data'; import { isServerWithId } from '../../../servers/data'; @@ -20,7 +20,7 @@ interface ExportShortUrlsBtnConnectProps extends ExportShortUrlsBtnProps { const itemsPerPage = 20; export const ExportShortUrlsBtn = ( - buildShlinkApiClient: ShlinkApiClientBuilder, + apiClient: ShlinkApiClient, { exportShortUrls }: ReportExporter, ): FC => ({ amount = 0, selectedServer }) => { const [{ tags, search, startDate, endDate, orderBy, tagsMode }] = useShortUrlsQuery(); @@ -31,9 +31,8 @@ export const ExportShortUrlsBtn = ( } const totalPages = amount / itemsPerPage; - const { listShortUrls } = buildShlinkApiClient(selectedServer); const loadAllUrls = async (page = 1): Promise => { - const { data } = await listShortUrls( + const { data } = await apiClient.listShortUrls( { page: `${page}`, tags, searchTerm: search, startDate, endDate, orderBy, tagsMode, itemsPerPage }, ); diff --git a/src/shlink-web-component/short-urls/reducers/shortUrlCreation.ts b/src/shlink-web-component/short-urls/reducers/shortUrlCreation.ts index 00116624..777f02f5 100644 --- a/src/shlink-web-component/short-urls/reducers/shortUrlCreation.ts +++ b/src/shlink-web-component/short-urls/reducers/shortUrlCreation.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ProblemDetailsError } from '../../../api/types/errors'; import { parseApiError } from '../../../api/utils'; import { createAsyncThunk } from '../../../utils/helpers/redux'; @@ -36,9 +36,9 @@ const initialState: ShortUrlCreation = { error: false, }; -export const createShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk( +export const createShortUrl = (apiClient: ShlinkApiClient) => createAsyncThunk( `${REDUCER_PREFIX}/createShortUrl`, - (data: ShortUrlData, { getState }): Promise => buildShlinkApiClient(getState).createShortUrl(data), + (data: ShortUrlData): Promise => apiClient.createShortUrl(data), ); export const shortUrlCreationReducerCreator = (createShortUrlThunk: ReturnType) => { diff --git a/src/shlink-web-component/short-urls/reducers/shortUrlDeletion.ts b/src/shlink-web-component/short-urls/reducers/shortUrlDeletion.ts index 3298e80b..6a7c4591 100644 --- a/src/shlink-web-component/short-urls/reducers/shortUrlDeletion.ts +++ b/src/shlink-web-component/short-urls/reducers/shortUrlDeletion.ts @@ -1,5 +1,5 @@ import { createAction, createSlice } from '@reduxjs/toolkit'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ProblemDetailsError } from '../../../api/types/errors'; import { parseApiError } from '../../../api/utils'; import { createAsyncThunk } from '../../../utils/helpers/redux'; @@ -22,11 +22,10 @@ const initialState: ShortUrlDeletion = { error: false, }; -export const deleteShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk( +export const deleteShortUrl = (apiClient: ShlinkApiClient) => createAsyncThunk( `${REDUCER_PREFIX}/deleteShortUrl`, - async ({ shortCode, domain }: ShortUrlIdentifier, { getState }): Promise => { - const { deleteShortUrl: shlinkDeleteShortUrl } = buildShlinkApiClient(getState); - await shlinkDeleteShortUrl(shortCode, domain); + async ({ shortCode, domain }: ShortUrlIdentifier): Promise => { + await apiClient.deleteShortUrl(shortCode, domain); return { shortCode, domain }; }, ); diff --git a/src/shlink-web-component/short-urls/reducers/shortUrlDetail.ts b/src/shlink-web-component/short-urls/reducers/shortUrlDetail.ts index 14c09dec..2b08d267 100644 --- a/src/shlink-web-component/short-urls/reducers/shortUrlDetail.ts +++ b/src/shlink-web-component/short-urls/reducers/shortUrlDetail.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ProblemDetailsError } from '../../../api/types/errors'; import { parseApiError } from '../../../api/utils'; import { createAsyncThunk } from '../../../utils/helpers/redux'; @@ -23,14 +23,14 @@ const initialState: ShortUrlDetail = { error: false, }; -export const shortUrlDetailReducerCreator = (buildShlinkApiClient: ShlinkApiClientBuilder) => { +export const shortUrlDetailReducerCreator = (apiClient: ShlinkApiClient) => { const getShortUrlDetail = createAsyncThunk( `${REDUCER_PREFIX}/getShortUrlDetail`, async ({ shortCode, domain }: ShortUrlIdentifier, { getState }): Promise => { const { shortUrlsList } = getState(); const alreadyLoaded = shortUrlsList?.shortUrls?.data.find((url) => shortUrlMatches(url, shortCode, domain)); - return alreadyLoaded ?? await buildShlinkApiClient(getState).getShortUrl(shortCode, domain); + return alreadyLoaded ?? await apiClient.getShortUrl(shortCode, domain); }, ); diff --git a/src/shlink-web-component/short-urls/reducers/shortUrlEdition.ts b/src/shlink-web-component/short-urls/reducers/shortUrlEdition.ts index 50f147b4..69128060 100644 --- a/src/shlink-web-component/short-urls/reducers/shortUrlEdition.ts +++ b/src/shlink-web-component/short-urls/reducers/shortUrlEdition.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ProblemDetailsError } from '../../../api/types/errors'; import { parseApiError } from '../../../api/utils'; import { createAsyncThunk } from '../../../utils/helpers/redux'; @@ -28,12 +28,11 @@ const initialState: ShortUrlEdition = { error: false, }; -export const editShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk( +export const editShortUrl = (apiClient: ShlinkApiClient) => createAsyncThunk( `${REDUCER_PREFIX}/editShortUrl`, - ({ shortCode, domain, data }: EditShortUrl, { getState }): Promise => { - const { updateShortUrl } = buildShlinkApiClient(getState); - return updateShortUrl(shortCode, domain, data as any); // FIXME parse dates - }, + ({ shortCode, domain, data }: EditShortUrl): Promise => + apiClient.updateShortUrl(shortCode, domain, data as any) // FIXME parse dates + , ); export const shortUrlEditionReducerCreator = (editShortUrlThunk: ReturnType) => createSlice({ diff --git a/src/shlink-web-component/short-urls/reducers/shortUrlsList.ts b/src/shlink-web-component/short-urls/reducers/shortUrlsList.ts index f8a8500f..ade20b4b 100644 --- a/src/shlink-web-component/short-urls/reducers/shortUrlsList.ts +++ b/src/shlink-web-component/short-urls/reducers/shortUrlsList.ts @@ -1,6 +1,6 @@ import { createSlice } from '@reduxjs/toolkit'; import { assocPath, last, pipe, reject } from 'ramda'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ShlinkShortUrlsListParams, ShlinkShortUrlsResponse } from '../../../api/types'; import { createAsyncThunk } from '../../../utils/helpers/redux'; import { createNewVisits } from '../../visits/reducers/visitCreation'; @@ -24,11 +24,16 @@ const initialState: ShortUrlsList = { error: false, }; -export const listShortUrls = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk( +export const listShortUrls = (apiClient: ShlinkApiClient) => createAsyncThunk( `${REDUCER_PREFIX}/listShortUrls`, - (params: ShlinkShortUrlsListParams | void, { getState }): Promise => { - const { listShortUrls: shlinkListShortUrls } = buildShlinkApiClient(getState); - return shlinkListShortUrls(params ?? {}); + (params: ShlinkShortUrlsListParams | void): Promise => { + try { + const { listShortUrls: shlinkListShortUrls } = apiClient; + return shlinkListShortUrls(params ?? {}); + } catch (e) { + console.log(e); + throw e; + } }, ); diff --git a/src/shlink-web-component/short-urls/services/provideServices.ts b/src/shlink-web-component/short-urls/services/provideServices.ts index d5e76d67..10e6fe45 100644 --- a/src/shlink-web-component/short-urls/services/provideServices.ts +++ b/src/shlink-web-component/short-urls/services/provideServices.ts @@ -54,7 +54,7 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('QrCodeModal', QrCodeModal, 'ImageDownloader'); bottle.serviceFactory('ShortUrlsFilteringBar', ShortUrlsFilteringBar, 'ExportShortUrlsBtn', 'TagsSelector'); - bottle.serviceFactory('ExportShortUrlsBtn', ExportShortUrlsBtn, 'buildShlinkApiClient', 'ReportExporter'); + bottle.serviceFactory('ExportShortUrlsBtn', ExportShortUrlsBtn, 'apiClient', 'ReportExporter'); bottle.decorator('ExportShortUrlsBtn', connect(['selectedServer'])); // Reducers @@ -76,20 +76,20 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('shortUrlDeletionReducerCreator', shortUrlDeletionReducerCreator, 'deleteShortUrl'); bottle.serviceFactory('shortUrlDeletionReducer', prop('reducer'), 'shortUrlDeletionReducerCreator'); - bottle.serviceFactory('shortUrlDetailReducerCreator', shortUrlDetailReducerCreator, 'buildShlinkApiClient'); + bottle.serviceFactory('shortUrlDetailReducerCreator', shortUrlDetailReducerCreator, 'apiClient'); bottle.serviceFactory('shortUrlDetailReducer', prop('reducer'), 'shortUrlDetailReducerCreator'); // Actions - bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient'); + bottle.serviceFactory('listShortUrls', listShortUrls, 'apiClient'); - bottle.serviceFactory('createShortUrl', createShortUrl, 'buildShlinkApiClient'); + bottle.serviceFactory('createShortUrl', createShortUrl, 'apiClient'); bottle.serviceFactory('resetCreateShortUrl', prop('resetCreateShortUrl'), 'shortUrlCreationReducerCreator'); - bottle.serviceFactory('deleteShortUrl', deleteShortUrl, 'buildShlinkApiClient'); + bottle.serviceFactory('deleteShortUrl', deleteShortUrl, 'apiClient'); bottle.serviceFactory('resetDeleteShortUrl', prop('resetDeleteShortUrl'), 'shortUrlDeletionReducerCreator'); bottle.serviceFactory('shortUrlDeleted', () => shortUrlDeleted); bottle.serviceFactory('getShortUrlDetail', prop('getShortUrlDetail'), 'shortUrlDetailReducerCreator'); - bottle.serviceFactory('editShortUrl', editShortUrl, 'buildShlinkApiClient'); + bottle.serviceFactory('editShortUrl', editShortUrl, 'apiClient'); }; diff --git a/src/shlink-web-component/tags/reducers/tagDelete.ts b/src/shlink-web-component/tags/reducers/tagDelete.ts index 8c86aaea..d87bc0fc 100644 --- a/src/shlink-web-component/tags/reducers/tagDelete.ts +++ b/src/shlink-web-component/tags/reducers/tagDelete.ts @@ -1,5 +1,5 @@ import { createAction, createSlice } from '@reduxjs/toolkit'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ProblemDetailsError } from '../../../api/types/errors'; import { parseApiError } from '../../../api/utils'; import { createAsyncThunk } from '../../../utils/helpers/redux'; @@ -21,10 +21,9 @@ const initialState: TagDeletion = { export const tagDeleted = createAction(`${REDUCER_PREFIX}/tagDeleted`); -export const tagDeleteReducerCreator = (buildShlinkApiClient: ShlinkApiClientBuilder) => { - const deleteTag = createAsyncThunk(`${REDUCER_PREFIX}/deleteTag`, async (tag: string, { getState }): Promise => { - const { deleteTags } = buildShlinkApiClient(getState); - await deleteTags([tag]); +export const tagDeleteReducerCreator = (apiClient: ShlinkApiClient) => { + const deleteTag = createAsyncThunk(`${REDUCER_PREFIX}/deleteTag`, async (tag: string): Promise => { + await apiClient.deleteTags([tag]); }); const { reducer } = createSlice({ diff --git a/src/shlink-web-component/tags/reducers/tagEdit.ts b/src/shlink-web-component/tags/reducers/tagEdit.ts index b3ae6aad..410c9421 100644 --- a/src/shlink-web-component/tags/reducers/tagEdit.ts +++ b/src/shlink-web-component/tags/reducers/tagEdit.ts @@ -1,7 +1,7 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createAction, createSlice } from '@reduxjs/toolkit'; import { pick } from 'ramda'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ProblemDetailsError } from '../../../api/types/errors'; import { parseApiError } from '../../../api/utils'; import { createAsyncThunk } from '../../../utils/helpers/redux'; @@ -35,12 +35,12 @@ const initialState: TagEdition = { export const tagEdited = createAction(`${REDUCER_PREFIX}/tagEdited`); export const editTag = ( - buildShlinkApiClient: ShlinkApiClientBuilder, + apiClient: ShlinkApiClient, colorGenerator: ColorGenerator, ) => createAsyncThunk( `${REDUCER_PREFIX}/editTag`, - async ({ oldName, newName, color }: EditTag, { getState }): Promise => { - await buildShlinkApiClient(getState).editTag(oldName, newName); + async ({ oldName, newName, color }: EditTag): Promise => { + await apiClient.editTag(oldName, newName); colorGenerator.setColorForKey(newName, color); return { oldName, newName, color }; diff --git a/src/shlink-web-component/tags/reducers/tagsList.ts b/src/shlink-web-component/tags/reducers/tagsList.ts index d915d038..e5ff049d 100644 --- a/src/shlink-web-component/tags/reducers/tagsList.ts +++ b/src/shlink-web-component/tags/reducers/tagsList.ts @@ -1,6 +1,6 @@ import { createAction, createSlice } from '@reduxjs/toolkit'; import { isEmpty, reject } from 'ramda'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ShlinkTags } from '../../../api/types'; import type { ProblemDetailsError } from '../../../api/types/errors'; import { parseApiError } from '../../../api/utils'; @@ -84,7 +84,7 @@ const calculateVisitsPerTag = (createdVisits: CreateVisit[]): TagIncrease[] => O }, {}), ); -export const listTags = (buildShlinkApiClient: ShlinkApiClientBuilder, force = true) => createAsyncThunk( +export const listTags = (apiClient: ShlinkApiClient, force = true) => createAsyncThunk( `${REDUCER_PREFIX}/listTags`, async (_: void, { getState }): Promise => { const { tagsList, selectedServer } = getState(); @@ -93,11 +93,10 @@ export const listTags = (buildShlinkApiClient: ShlinkApiClientBuilder, force = t return tagsList; } - const { listTags: shlinkListTags, tagsStats } = buildShlinkApiClient(getState); const { tags, stats }: ShlinkTags = await ( isReachableServer(selectedServer) && isFeatureEnabledForVersion('tagsStats', selectedServer.version) - ? tagsStats() - : shlinkListTags() + ? apiClient.tagsStats() + : apiClient.listTags() ); const processedStats = stats.reduce((acc, { tag, ...rest }) => { acc[tag] = rest; diff --git a/src/shlink-web-component/tags/services/provideServices.ts b/src/shlink-web-component/tags/services/provideServices.ts index a9b426cc..e9c85a0b 100644 --- a/src/shlink-web-component/tags/services/provideServices.ts +++ b/src/shlink-web-component/tags/services/provideServices.ts @@ -36,15 +36,14 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('tagEditReducerCreator', tagEditReducerCreator, 'editTag'); bottle.serviceFactory('tagEditReducer', prop('reducer'), 'tagEditReducerCreator'); - bottle.serviceFactory('tagDeleteReducerCreator', tagDeleteReducerCreator, 'buildShlinkApiClient'); + bottle.serviceFactory('tagDeleteReducerCreator', tagDeleteReducerCreator, 'apiClient'); bottle.serviceFactory('tagDeleteReducer', prop('reducer'), 'tagDeleteReducerCreator'); bottle.serviceFactory('tagsListReducerCreator', tagsListReducerCreator, 'listTags', 'createShortUrl'); bottle.serviceFactory('tagsListReducer', prop('reducer'), 'tagsListReducerCreator'); // Actions - const listTagsActionFactory = (force: boolean) => - ({ buildShlinkApiClient }: IContainer) => listTags(buildShlinkApiClient, force); + const listTagsActionFactory = (force: boolean) => ({ apiClient }: IContainer) => listTags(apiClient, force); bottle.factory('listTags', listTagsActionFactory(false)); bottle.factory('forceListTags', listTagsActionFactory(true)); @@ -53,6 +52,6 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('deleteTag', prop('deleteTag'), 'tagDeleteReducerCreator'); bottle.serviceFactory('tagDeleted', () => tagDeleted); - bottle.serviceFactory('editTag', editTag, 'buildShlinkApiClient', 'ColorGenerator'); + bottle.serviceFactory('editTag', editTag, 'apiClient', 'ColorGenerator'); bottle.serviceFactory('tagEdited', () => tagEdited); }; diff --git a/src/shlink-web-component/visits/reducers/common.ts b/src/shlink-web-component/visits/reducers/common.ts index 68e3d6a8..0c57cb3c 100644 --- a/src/shlink-web-component/visits/reducers/common.ts +++ b/src/shlink-web-component/visits/reducers/common.ts @@ -22,7 +22,7 @@ type LastVisitLoader = (excludeBots?: boolean) => Promise; interface VisitsAsyncThunkOptions { typePrefix: string; - createLoaders: (params: T, getState: () => ShlinkState) => [VisitsLoader, LastVisitLoader]; + createLoaders: (params: T) => [VisitsLoader, LastVisitLoader]; getExtraFulfilledPayload: (params: T) => Partial; shouldCancel: (getState: () => ShlinkState) => boolean; } @@ -35,7 +35,7 @@ export const createVisitsAsyncThunk = (`${typePrefix}/fallbackToInterval`); const asyncThunk = createAsyncThunk(typePrefix, async (params: T, { getState, dispatch }): Promise> => { - const [visitsLoader, lastVisitLoader] = createLoaders(params, getState); + const [visitsLoader, lastVisitLoader] = createLoaders(params); const loadVisitsInParallel = async (pages: number[]): Promise => Promise.all(pages.map(async (page) => visitsLoader(page, ITEMS_PER_PAGE).then(prop('data')))).then(flatten); diff --git a/src/shlink-web-component/visits/reducers/domainVisits.ts b/src/shlink-web-component/visits/reducers/domainVisits.ts index 97094be6..ec67e4cf 100644 --- a/src/shlink-web-component/visits/reducers/domainVisits.ts +++ b/src/shlink-web-component/visits/reducers/domainVisits.ts @@ -1,4 +1,4 @@ -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import { isBetween } from '../../../utils/helpers/date'; import { domainMatches } from '../../short-urls/helpers'; import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common'; @@ -26,10 +26,10 @@ const initialState: DomainVisits = { progress: 0, }; -export const getDomainVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({ +export const getDomainVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({ typePrefix: `${REDUCER_PREFIX}/getDomainVisits`, - createLoaders: ({ domain, query = {}, doIntervalFallback = false }: LoadDomainVisits, getState) => { - const { getDomainVisits: getVisits } = buildShlinkApiClient(getState); + createLoaders: ({ domain, query = {}, doIntervalFallback = false }: LoadDomainVisits) => { + const { getDomainVisits: getVisits } = apiClient; const visitsLoader = async (page: number, itemsPerPage: number) => getVisits( domain, { ...query, page, itemsPerPage }, diff --git a/src/shlink-web-component/visits/reducers/nonOrphanVisits.ts b/src/shlink-web-component/visits/reducers/nonOrphanVisits.ts index ed89508c..cc97c8fc 100644 --- a/src/shlink-web-component/visits/reducers/nonOrphanVisits.ts +++ b/src/shlink-web-component/visits/reducers/nonOrphanVisits.ts @@ -1,3 +1,4 @@ +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; import { isBetween } from '../../../utils/helpers/date'; import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common'; @@ -14,10 +15,10 @@ const initialState: VisitsInfo = { progress: 0, }; -export const getNonOrphanVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({ +export const getNonOrphanVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({ typePrefix: `${REDUCER_PREFIX}/getNonOrphanVisits`, - createLoaders: ({ query = {}, doIntervalFallback = false }, getState) => { - const { getNonOrphanVisits: shlinkGetNonOrphanVisits } = buildShlinkApiClient(getState); + createLoaders: ({ query = {}, doIntervalFallback = false }) => { + const { getNonOrphanVisits: shlinkGetNonOrphanVisits } = apiClient; const visitsLoader = async (page: number, itemsPerPage: number) => shlinkGetNonOrphanVisits({ ...query, page, itemsPerPage }); const lastVisitLoader = lastVisitLoaderForLoader(doIntervalFallback, shlinkGetNonOrphanVisits); diff --git a/src/shlink-web-component/visits/reducers/orphanVisits.ts b/src/shlink-web-component/visits/reducers/orphanVisits.ts index 22f7a190..82cf869a 100644 --- a/src/shlink-web-component/visits/reducers/orphanVisits.ts +++ b/src/shlink-web-component/visits/reducers/orphanVisits.ts @@ -1,3 +1,4 @@ +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; import { isBetween } from '../../../utils/helpers/date'; import type { OrphanVisit, OrphanVisitType } from '../types'; @@ -23,10 +24,10 @@ const initialState: VisitsInfo = { const matchesType = (visit: OrphanVisit, orphanVisitsType?: OrphanVisitType) => !orphanVisitsType || orphanVisitsType === visit.type; -export const getOrphanVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({ +export const getOrphanVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({ typePrefix: `${REDUCER_PREFIX}/getOrphanVisits`, - createLoaders: ({ orphanVisitsType, query = {}, doIntervalFallback = false }: LoadOrphanVisits, getState) => { - const { getOrphanVisits: getVisits } = buildShlinkApiClient(getState); + createLoaders: ({ orphanVisitsType, query = {}, doIntervalFallback = false }: LoadOrphanVisits) => { + const { getOrphanVisits: getVisits } = apiClient; const visitsLoader = async (page: number, itemsPerPage: number) => getVisits({ ...query, page, itemsPerPage }) .then((result) => { const visits = result.data.filter((visit) => isOrphanVisit(visit) && matchesType(visit, orphanVisitsType)); diff --git a/src/shlink-web-component/visits/reducers/shortUrlVisits.ts b/src/shlink-web-component/visits/reducers/shortUrlVisits.ts index 60865b82..390da505 100644 --- a/src/shlink-web-component/visits/reducers/shortUrlVisits.ts +++ b/src/shlink-web-component/visits/reducers/shortUrlVisits.ts @@ -1,4 +1,4 @@ -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import { isBetween } from '../../../utils/helpers/date'; import type { ShortUrlIdentifier } from '../../short-urls/data'; import { shortUrlMatches } from '../../short-urls/helpers'; @@ -24,10 +24,10 @@ const initialState: ShortUrlVisits = { progress: 0, }; -export const getShortUrlVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({ +export const getShortUrlVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({ typePrefix: `${REDUCER_PREFIX}/getShortUrlVisits`, - createLoaders: ({ shortCode, query = {}, doIntervalFallback = false }: LoadShortUrlVisits, getState) => { - const { getShortUrlVisits: shlinkGetShortUrlVisits } = buildShlinkApiClient(getState); + createLoaders: ({ shortCode, query = {}, doIntervalFallback = false }: LoadShortUrlVisits) => { + const { getShortUrlVisits: shlinkGetShortUrlVisits } = apiClient; const visitsLoader = async (page: number, itemsPerPage: number) => shlinkGetShortUrlVisits( shortCode, { ...query, page, itemsPerPage }, diff --git a/src/shlink-web-component/visits/reducers/tagVisits.ts b/src/shlink-web-component/visits/reducers/tagVisits.ts index 66ed3694..e2e1b7c4 100644 --- a/src/shlink-web-component/visits/reducers/tagVisits.ts +++ b/src/shlink-web-component/visits/reducers/tagVisits.ts @@ -1,4 +1,4 @@ -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import { isBetween } from '../../../utils/helpers/date'; import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common'; import type { LoadVisits, VisitsInfo } from './types'; @@ -23,10 +23,10 @@ const initialState: TagVisits = { progress: 0, }; -export const getTagVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({ +export const getTagVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({ typePrefix: `${REDUCER_PREFIX}/getTagVisits`, - createLoaders: ({ tag, query = {}, doIntervalFallback = false }: LoadTagVisits, getState) => { - const { getTagVisits: getVisits } = buildShlinkApiClient(getState); + createLoaders: ({ tag, query = {}, doIntervalFallback = false }: LoadTagVisits) => { + const { getTagVisits: getVisits } = apiClient; const visitsLoader = async (page: number, itemsPerPage: number) => getVisits( tag, { ...query, page, itemsPerPage }, diff --git a/src/shlink-web-component/visits/reducers/visitsOverview.ts b/src/shlink-web-component/visits/reducers/visitsOverview.ts index fb6c59dd..ec71568a 100644 --- a/src/shlink-web-component/visits/reducers/visitsOverview.ts +++ b/src/shlink-web-component/visits/reducers/visitsOverview.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder'; +import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient'; import type { ShlinkVisitsOverview } from '../../../api/types'; import { createAsyncThunk } from '../../../utils/helpers/redux'; import type { CreateVisit } from '../types'; @@ -40,19 +40,19 @@ const initialState: VisitsOverview = { const countBots = (visits: CreateVisit[]) => visits.filter(({ visit }) => visit.potentialBot).length; -export const loadVisitsOverview = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk( +export const loadVisitsOverview = (apiClient: ShlinkApiClient) => createAsyncThunk( `${REDUCER_PREFIX}/loadVisitsOverview`, - (_: void, { getState }): Promise => buildShlinkApiClient(getState).getVisitsOverview().then( - (resp) => ({ + (): Promise => apiClient.getVisitsOverview().then( + ({ nonOrphanVisits, visitsCount, orphanVisits, orphanVisitsCount }) => ({ nonOrphanVisits: { - total: resp.nonOrphanVisits?.total ?? resp.visitsCount, - nonBots: resp.nonOrphanVisits?.nonBots, - bots: resp.nonOrphanVisits?.bots, + total: nonOrphanVisits?.total ?? visitsCount, + nonBots: nonOrphanVisits?.nonBots, + bots: nonOrphanVisits?.bots, }, orphanVisits: { - total: resp.orphanVisits?.total ?? resp.orphanVisitsCount, - nonBots: resp.orphanVisits?.nonBots, - bots: resp.orphanVisits?.bots, + total: orphanVisits?.total ?? orphanVisitsCount, + nonBots: orphanVisits?.nonBots, + bots: orphanVisits?.bots, }, }), ), diff --git a/src/shlink-web-component/visits/services/provideServices.ts b/src/shlink-web-component/visits/services/provideServices.ts index 53b0b670..c1626596 100644 --- a/src/shlink-web-component/visits/services/provideServices.ts +++ b/src/shlink-web-component/visits/services/provideServices.ts @@ -54,23 +54,23 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('VisitsParser', () => visitsParser); // Actions - bottle.serviceFactory('getShortUrlVisits', getShortUrlVisits, 'buildShlinkApiClient'); + bottle.serviceFactory('getShortUrlVisits', getShortUrlVisits, 'apiClient'); bottle.serviceFactory('cancelGetShortUrlVisits', prop('cancelGetVisits'), 'shortUrlVisitsReducerCreator'); - bottle.serviceFactory('getTagVisits', getTagVisits, 'buildShlinkApiClient'); + bottle.serviceFactory('getTagVisits', getTagVisits, 'apiClient'); bottle.serviceFactory('cancelGetTagVisits', prop('cancelGetVisits'), 'tagVisitsReducerCreator'); - bottle.serviceFactory('getDomainVisits', getDomainVisits, 'buildShlinkApiClient'); + bottle.serviceFactory('getDomainVisits', getDomainVisits, 'apiClient'); bottle.serviceFactory('cancelGetDomainVisits', prop('cancelGetVisits'), 'domainVisitsReducerCreator'); - bottle.serviceFactory('getOrphanVisits', getOrphanVisits, 'buildShlinkApiClient'); + bottle.serviceFactory('getOrphanVisits', getOrphanVisits, 'apiClient'); bottle.serviceFactory('cancelGetOrphanVisits', prop('cancelGetVisits'), 'orphanVisitsReducerCreator'); - bottle.serviceFactory('getNonOrphanVisits', getNonOrphanVisits, 'buildShlinkApiClient'); + bottle.serviceFactory('getNonOrphanVisits', getNonOrphanVisits, 'apiClient'); bottle.serviceFactory('cancelGetNonOrphanVisits', prop('cancelGetVisits'), 'nonOrphanVisitsReducerCreator'); bottle.serviceFactory('createNewVisits', () => createNewVisits); - bottle.serviceFactory('loadVisitsOverview', loadVisitsOverview, 'buildShlinkApiClient'); + bottle.serviceFactory('loadVisitsOverview', loadVisitsOverview, 'apiClient'); // Reducers bottle.serviceFactory('visitsOverviewReducerCreator', visitsOverviewReducerCreator, 'loadVisitsOverview');