Refactor DI approach for components

This commit is contained in:
Alejandro Celaya
2023-09-05 09:08:42 +02:00
parent 046f79270a
commit 6926afbac1
30 changed files with 371 additions and 234 deletions

View File

@@ -2,9 +2,11 @@ import { faFileUpload as importIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useElementRef, useToggle } from '@shlinkio/shlink-frontend-kit';
import { complement } from 'ramda';
import type { ChangeEvent, FC, PropsWithChildren } from 'react';
import type { ChangeEvent, PropsWithChildren } from 'react';
import { useCallback, useRef, useState } from 'react';
import { Button, UncontrolledTooltip } from 'reactstrap';
import type { FCWithDeps } from '../../container/utils';
import { componentFactory, useDependencies } from '../../container/utils';
import type { ServerData, ServersMap } from '../data';
import type { ServersImporter } from '../services/ServersImporter';
import { DuplicatedServersModal } from './DuplicatedServersModal';
@@ -16,15 +18,19 @@ export type ImportServersBtnProps = PropsWithChildren<{
className?: string;
}>;
interface ImportServersBtnConnectProps extends ImportServersBtnProps {
type ImportServersBtnConnectProps = ImportServersBtnProps & {
createServers: (servers: ServerData[]) => void;
servers: ServersMap;
}
};
type ImportServersBtnDeps = {
ServersImporter: ServersImporter
};
const serversFiltering = (servers: ServerData[]) =>
({ url, apiKey }: ServerData) => servers.some((server) => server.url === url && server.apiKey === apiKey);
export const ImportServersBtn = (serversImporter: ServersImporter): FC<ImportServersBtnConnectProps> => ({
const ImportServersBtn: FCWithDeps<ImportServersBtnConnectProps, ImportServersBtnDeps> = ({
createServers,
servers,
children,
@@ -33,6 +39,7 @@ export const ImportServersBtn = (serversImporter: ServersImporter): FC<ImportSer
tooltipPlacement = 'bottom',
className = '',
}) => {
const { ServersImporter: serversImporter } = useDependencies(ImportServersBtn);
const ref = useElementRef<HTMLInputElement>();
const [duplicatedServers, setDuplicatedServers] = useState<ServerData[]>([]);
const [isModalOpen,, showModal, hideModal] = useToggle();
@@ -60,7 +67,7 @@ export const ImportServersBtn = (serversImporter: ServersImporter): FC<ImportSer
(target as { value: string | null }).value = null; // eslint-disable-line no-param-reassign
})
.catch(onImportError),
[create, onImportError, servers, showModal],
[create, onImportError, servers, serversImporter, showModal],
);
const createAllServers = useCallback(() => {
@@ -92,3 +99,5 @@ export const ImportServersBtn = (serversImporter: ServersImporter): FC<ImportSer
</>
);
};
export const ImportServersBtnFactory = componentFactory(ImportServersBtn, ['ServersImporter']);

View File

@@ -2,46 +2,56 @@ import { Message } from '@shlinkio/shlink-frontend-kit';
import type { FC } from 'react';
import { Link } from 'react-router-dom';
import { NoMenuLayout } from '../../common/NoMenuLayout';
import type { FCWithDeps } from '../../container/utils';
import { componentFactory, useDependencies } from '../../container/utils';
import type { SelectedServer, ServersMap } from '../data';
import { isServerWithId } from '../data';
import type { DeleteServerButtonProps } from '../DeleteServerButton';
import { ServersListGroup } from '../ServersListGroup';
import './ServerError.scss';
interface ServerErrorProps {
type ServerErrorProps = {
servers: ServersMap;
selectedServer: SelectedServer;
}
};
type ServerErrorDeps = {
DeleteServerButton: FC<DeleteServerButtonProps>;
};
const ServerError: FCWithDeps<ServerErrorProps, ServerErrorDeps> = ({ servers, selectedServer }) => {
const { DeleteServerButton } = useDependencies(ServerError);
return (
<NoMenuLayout>
<div className="server-error__container flex-column">
<Message className="w-100 mb-3 mb-md-5" type="error" fullWidth>
{!isServerWithId(selectedServer) && 'Could not find this Shlink server.'}
{isServerWithId(selectedServer) && (
<>
<p>Oops! Could not connect to this Shlink server.</p>
Make sure you have internet connection, and the server is properly configured and on-line.
</>
)}
</Message>
<ServersListGroup servers={Object.values(servers)}>
These are the Shlink servers currently configured. Choose one of
them or <Link to="/server/create">add a new one</Link>.
</ServersListGroup>
export const ServerError = (DeleteServerButton: FC<DeleteServerButtonProps>): FC<ServerErrorProps> => (
{ servers, selectedServer },
) => (
<NoMenuLayout>
<div className="server-error__container flex-column">
<Message className="w-100 mb-3 mb-md-5" type="error" fullWidth>
{!isServerWithId(selectedServer) && 'Could not find this Shlink server.'}
{isServerWithId(selectedServer) && (
<>
<p>Oops! Could not connect to this Shlink server.</p>
Make sure you have internet connection, and the server is properly configured and on-line.
</>
<div className="container mt-3 mt-md-5">
<h5>
Alternatively, if you think you may have miss-configured this server, you
can <DeleteServerButton server={selectedServer} className="server-error__delete-btn">remove it</DeleteServerButton> or&nbsp;
<Link to={`/server/${selectedServer.id}/edit?reconnect=true`}>edit it</Link>.
</h5>
</div>
)}
</Message>
</div>
</NoMenuLayout>
);
};
<ServersListGroup servers={Object.values(servers)}>
These are the Shlink servers currently configured. Choose one of
them or <Link to="/server/create">add a new one</Link>.
</ServersListGroup>
{isServerWithId(selectedServer) && (
<div className="container mt-3 mt-md-5">
<h5>
Alternatively, if you think you may have miss-configured this server, you
can <DeleteServerButton server={selectedServer} className="server-error__delete-btn">remove it</DeleteServerButton> or&nbsp;
<Link to={`/server/${selectedServer.id}/edit?reconnect=true`}>edit it</Link>.
</h5>
</div>
)}
</div>
</NoMenuLayout>
);
export const ServerErrorFactory = componentFactory(ServerError, ['DeleteServerButton']);

View File

@@ -3,16 +3,25 @@ import type { FC } from 'react';
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { NoMenuLayout } from '../../common/NoMenuLayout';
import type { FCWithDeps } from '../../container/utils';
import { useDependencies } from '../../container/utils';
import type { SelectedServer } from '../data';
import { isNotFoundServer } from '../data';
interface WithSelectedServerProps {
export type WithSelectedServerProps = {
selectServer: (serverId: string) => void;
selectedServer: SelectedServer;
}
};
export function withSelectedServer<T = {}>(WrappedComponent: FC<WithSelectedServerProps & T>, ServerError: FC) {
return (props: WithSelectedServerProps & T) => {
type WithSelectedServerPropsDeps = {
ServerError: FC;
};
export function withSelectedServer<T = {}>(
WrappedComponent: FCWithDeps<WithSelectedServerProps & T, WithSelectedServerPropsDeps>,
) {
const ComponentWrapper: FCWithDeps<WithSelectedServerProps & T, WithSelectedServerPropsDeps> = (props) => {
const { ServerError } = useDependencies(ComponentWrapper);
const params = useParams<{ serverId: string }>();
const { selectServer, selectedServer } = props;
@@ -34,4 +43,5 @@ export function withSelectedServer<T = {}>(WrappedComponent: FC<WithSelectedServ
return <WrappedComponent {...props} />;
};
return ComponentWrapper;
}