shlink-web-client/src/short-urls/helpers/QrCodeModal.tsx

110 lines
4.5 KiB
TypeScript
Raw Normal View History

import { useMemo, useState } from 'react';
import { Modal, FormGroup, ModalBody, ModalHeader, Row, Button } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFileDownload as downloadIcon } from '@fortawesome/free-solid-svg-icons';
import { ExternalLink } from 'react-external-link';
import { ShortUrlModalProps } from '../data';
2021-03-06 11:58:29 +03:00
import { SelectedServer } from '../../servers/data';
import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon';
import { buildQrCodeUrl, QrCodeCapabilities, QrCodeFormat, QrErrorCorrection } from '../../utils/helpers/qrCodes';
import { supportsNonRestCors, supportsQrErrorCorrection } from '../../utils/helpers/features';
import { ImageDownloader } from '../../common/services/ImageDownloader';
import { QrFormatDropdown } from './qr-codes/QrFormatDropdown';
import { QrErrorCorrectionDropdown } from './qr-codes/QrErrorCorrectionDropdown';
import './QrCodeModal.scss';
2021-01-21 18:51:54 +03:00
interface QrCodeModalConnectProps extends ShortUrlModalProps {
2021-03-06 11:58:29 +03:00
selectedServer: SelectedServer;
2021-01-21 18:51:54 +03:00
}
export const QrCodeModal = (imageDownloader: ImageDownloader) => (
{ shortUrl: { shortUrl, shortCode }, toggle, isOpen, selectedServer }: QrCodeModalConnectProps,
) => {
2022-03-26 14:17:42 +03:00
const [size, setSize] = useState(300);
const [margin, setMargin] = useState(0);
const [format, setFormat] = useState<QrCodeFormat>('png');
const [errorCorrection, setErrorCorrection] = useState<QrErrorCorrection>('L');
const capabilities: QrCodeCapabilities = useMemo(() => ({
errorCorrectionIsSupported: supportsQrErrorCorrection(selectedServer),
2022-03-26 14:17:42 +03:00
}), [selectedServer]);
const displayDownloadBtn = supportsNonRestCors(selectedServer);
const willRenderThreeControls = !capabilities.errorCorrectionIsSupported;
2021-01-21 18:51:54 +03:00
const qrCodeUrl = useMemo(
() => buildQrCodeUrl(shortUrl, { size, format, margin, errorCorrection }, capabilities),
2022-03-26 14:17:42 +03:00
[shortUrl, size, format, margin, errorCorrection, capabilities],
2021-01-21 18:51:54 +03:00
);
2022-03-26 14:17:42 +03:00
const totalSize = useMemo(() => size + margin, [size, margin]);
2021-01-21 18:51:54 +03:00
const modalSize = useMemo(() => {
if (totalSize < 500) {
2021-01-21 18:51:54 +03:00
return undefined;
}
return totalSize < 800 ? 'lg' : 'xl';
2022-03-26 14:17:42 +03:00
}, [totalSize]);
2021-01-21 18:51:54 +03:00
return (
2021-02-27 10:52:10 +03:00
<Modal isOpen={isOpen} toggle={toggle} centered size={modalSize}>
2021-01-21 18:51:54 +03:00
<ModalHeader toggle={toggle}>
QR code for <ExternalLink href={shortUrl}>{shortUrl}</ExternalLink>
</ModalHeader>
<ModalBody>
<Row>
2022-03-05 21:43:10 +03:00
<FormGroup className={`d-grid ${willRenderThreeControls ? 'col-md-4' : 'col-md-6'}`}>
<label>Size: {size}px</label>
<input
type="range"
className="form-control-range"
value={size}
step={10}
min={50}
max={1000}
onChange={(e) => setSize(Number(e.target.value))}
/>
</FormGroup>
<FormGroup className={`d-grid ${willRenderThreeControls ? 'col-md-4' : 'col-md-6'}`}>
<label htmlFor="marginControl">Margin: {margin}px</label>
<input
id="marginControl"
type="range"
className="form-control-range"
value={margin}
step={1}
min={0}
max={100}
onChange={(e) => setMargin(Number(e.target.value))}
/>
</FormGroup>
<FormGroup className={willRenderThreeControls ? 'col-md-4' : 'col-md-6'}>
<QrFormatDropdown format={format} setFormat={setFormat} />
</FormGroup>
{capabilities.errorCorrectionIsSupported && (
<FormGroup className="col-md-6">
<QrErrorCorrectionDropdown errorCorrection={errorCorrection} setErrorCorrection={setErrorCorrection} />
</FormGroup>
)}
2021-01-21 18:51:54 +03:00
</Row>
<div className="text-center">
<div className="mb-3">
<ExternalLink href={qrCodeUrl} />
<CopyToClipboardIcon text={qrCodeUrl} />
2021-01-21 18:51:54 +03:00
</div>
<img src={qrCodeUrl} className="qr-code-modal__img" alt="QR code" />
{displayDownloadBtn && (
<div className="mt-3">
<Button
block
color="primary"
2022-03-26 14:17:42 +03:00
onClick={() => {
imageDownloader.saveImage(qrCodeUrl, `${shortCode}-qr-code.${format}`).catch(() => {});
}}
>
Download <FontAwesomeIcon icon={downloadIcon} className="ms-1" />
</Button>
</div>
)}
2021-01-21 18:51:54 +03:00
</div>
</ModalBody>
2021-02-27 10:52:10 +03:00
</Modal>
2021-01-21 18:51:54 +03:00
);
};