2023-07-23 19:30:59 +03:00
|
|
|
import type { Store } from '@reduxjs/toolkit';
|
2023-07-24 10:48:41 +03:00
|
|
|
import type Bottle from 'bottlejs';
|
|
|
|
import type { FC, ReactNode } from 'react';
|
|
|
|
import { useEffect, useRef, useState } from 'react';
|
2023-08-04 23:59:33 +03:00
|
|
|
import { Provider as ReduxStoreProvider } from 'react-redux';
|
2023-07-24 18:30:58 +03:00
|
|
|
import type { ShlinkApiClient } from './api-contract';
|
2023-07-16 23:54:49 +03:00
|
|
|
import { FeaturesProvider, useFeatures } from './utils/features';
|
2023-07-29 11:43:15 +03:00
|
|
|
import type { SemVer } from './utils/helpers/version';
|
2023-07-24 18:30:58 +03:00
|
|
|
import { RoutesPrefixProvider } from './utils/routesPrefix';
|
2023-08-06 14:54:57 +03:00
|
|
|
import type { TagColorsStorage } from './utils/services/TagColorsStorage';
|
2023-07-23 19:30:59 +03:00
|
|
|
import type { Settings } from './utils/settings';
|
|
|
|
import { SettingsProvider } from './utils/settings';
|
2023-07-16 23:54:49 +03:00
|
|
|
|
|
|
|
type ShlinkWebComponentProps = {
|
2023-08-06 14:54:57 +03:00
|
|
|
serverVersion: SemVer; // FIXME Consider making this optional and trying to resolve it if not set
|
2023-07-24 18:30:58 +03:00
|
|
|
apiClient: ShlinkApiClient;
|
2023-08-06 14:54:57 +03:00
|
|
|
tagColorsStorage?: TagColorsStorage;
|
2023-07-31 21:52:08 +03:00
|
|
|
routesPrefix?: string;
|
|
|
|
settings?: Settings;
|
|
|
|
createNotFound?: (nonPrefixedHomePath: string) => ReactNode;
|
2023-07-16 23:54:49 +03:00
|
|
|
};
|
|
|
|
|
2023-08-04 23:59:33 +03:00
|
|
|
// FIXME This allows to track the reference to be resolved by the container, but it's hacky and relies on not more than
|
2023-08-08 00:29:27 +03:00
|
|
|
// one ShlinkWebComponent rendered at the same time.
|
|
|
|
// Works for now, but should be addressed.
|
2023-07-26 21:04:50 +03:00
|
|
|
let apiClientRef: ShlinkApiClient;
|
|
|
|
|
2023-07-24 10:48:41 +03:00
|
|
|
export const createShlinkWebComponent = (
|
|
|
|
bottle: Bottle,
|
2023-08-08 00:29:27 +03:00
|
|
|
): FC<ShlinkWebComponentProps> => (
|
|
|
|
{ serverVersion, apiClient, settings, routesPrefix = '', createNotFound, tagColorsStorage },
|
|
|
|
) => {
|
2023-07-16 23:54:49 +03:00
|
|
|
const features = useFeatures(serverVersion);
|
2023-07-24 10:48:41 +03:00
|
|
|
const mainContent = useRef<ReactNode>();
|
|
|
|
const [theStore, setStore] = useState<Store | undefined>();
|
2023-07-16 23:54:49 +03:00
|
|
|
|
2023-07-24 10:48:41 +03:00
|
|
|
useEffect(() => {
|
2023-07-27 10:22:03 +03:00
|
|
|
apiClientRef = apiClient;
|
2023-07-26 21:04:50 +03:00
|
|
|
bottle.value('apiClientFactory', () => apiClientRef);
|
2023-07-16 23:54:49 +03:00
|
|
|
|
2023-08-06 14:54:57 +03:00
|
|
|
if (tagColorsStorage) {
|
|
|
|
bottle.value('TagColorsStorage', tagColorsStorage);
|
|
|
|
}
|
|
|
|
|
2023-07-24 10:48:41 +03:00
|
|
|
// 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;
|
2023-07-26 21:04:50 +03:00
|
|
|
const { Main, store, loadMercureInfo } = container;
|
2023-07-31 21:52:08 +03:00
|
|
|
mainContent.current = <Main createNotFound={createNotFound} />;
|
2023-07-26 21:04:50 +03:00
|
|
|
setStore(store);
|
|
|
|
|
|
|
|
// Load mercure info
|
2023-07-27 10:22:03 +03:00
|
|
|
store.dispatch(loadMercureInfo(settings));
|
2023-08-06 14:54:57 +03:00
|
|
|
}, [apiClient, tagColorsStorage]);
|
2023-07-24 10:48:41 +03:00
|
|
|
|
|
|
|
return !theStore ? <></> : (
|
2023-08-04 23:59:33 +03:00
|
|
|
<ReduxStoreProvider store={theStore}>
|
2023-07-23 19:30:59 +03:00
|
|
|
<SettingsProvider value={settings}>
|
|
|
|
<FeaturesProvider value={features}>
|
2023-07-24 18:30:58 +03:00
|
|
|
<RoutesPrefixProvider value={routesPrefix}>
|
|
|
|
{mainContent.current}
|
|
|
|
</RoutesPrefixProvider>
|
2023-07-23 19:30:59 +03:00
|
|
|
</FeaturesProvider>
|
|
|
|
</SettingsProvider>
|
2023-08-04 23:59:33 +03:00
|
|
|
</ReduxStoreProvider>
|
2023-07-16 23:54:49 +03:00
|
|
|
);
|
|
|
|
};
|