Improved QR code modal, to allow selecting size, format and copy URL

This commit is contained in:
Alejandro Celaya
2021-01-24 17:37:31 +01:00
parent 556495ea7e
commit 3546a17575
10 changed files with 106 additions and 47 deletions

View File

@@ -1,36 +1,28 @@
import { useMemo, useState } from 'react';
import { DropdownItem, FormGroup, Modal, ModalBody, ModalHeader, Row } from 'reactstrap';
import { ExternalLink } from 'react-external-link';
import CopyToClipboard from 'react-copy-to-clipboard';
import { faCopy as copyIcon } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ShortUrlModalProps } from '../data';
import { ReachableServer } from '../../servers/data';
import { versionMatch } from '../../utils/helpers/version';
import { DropdownBtn } from '../../utils/DropdownBtn';
import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon';
import { buildQrCodeUrl, QrCodeCapabilities, QrCodeFormat } from '../../utils/helpers/qrCodes';
import './QrCodeModal.scss';
interface QrCodeModalConnectProps extends ShortUrlModalProps {
selectedServer: ReachableServer;
}
type QrCodeFormat = 'svg' | 'png';
const buildQrCodeUrl = (shortUrl: string, size: number, format: QrCodeFormat, version: string): string => {
const useSizeInPath = !versionMatch(version, { minVersion: '2.5.0' });
const svgIsSupported = versionMatch(version, { minVersion: '2.4.0' });
const sizeFragment = useSizeInPath ? `/${size}?` : `?size=${size}&`;
const formatFragment = !svgIsSupported ? '' : `format=${format}`;
return `${shortUrl}/qr-code${sizeFragment}${formatFragment}`;
};
const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }: QrCodeModalConnectProps) => {
const [ size, setSize ] = useState(300);
const [ format, setFormat ] = useState<QrCodeFormat>('png');
const capabilities: QrCodeCapabilities = useMemo(() => ({
useSizeInPath: !versionMatch(selectedServer.version, { minVersion: '2.5.0' }),
svgIsSupported: versionMatch(selectedServer.version, { minVersion: '2.4.0' }),
}), [ selectedServer ]);
const qrCodeUrl = useMemo(
() => buildQrCodeUrl(shortUrl, size, format, selectedServer.version),
[ shortUrl, size, format, selectedServer ],
() => buildQrCodeUrl(shortUrl, size, format, capabilities),
[ shortUrl, size, format, capabilities ],
);
const modalSize = useMemo(() => {
if (size < 500) {
@@ -47,7 +39,7 @@ const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }:
</ModalHeader>
<ModalBody>
<Row className="mb-2">
<div className="col-md-6">
<div className={capabilities.svgIsSupported ? 'col-md-6' : 'col-12'}>
<FormGroup>
<label className="mb-0">Size: {size}px</label>
<input
@@ -61,20 +53,20 @@ const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }:
/>
</FormGroup>
</div>
<div className="col-md-6">
<DropdownBtn text={`Format (${format})`}>
<DropdownItem active={format === 'png'} onClick={() => setFormat('png')}>PNG</DropdownItem>
<DropdownItem active={format === 'svg'} onClick={() => setFormat('svg')}>SVG</DropdownItem>
</DropdownBtn>
</div>
{capabilities.svgIsSupported && (
<div className="col-md-6">
<DropdownBtn text={`Format (${format})`}>
<DropdownItem active={format === 'png'} onClick={() => setFormat('png')}>PNG</DropdownItem>
<DropdownItem active={format === 'svg'} onClick={() => setFormat('svg')}>SVG</DropdownItem>
</DropdownBtn>
</div>
)}
</Row>
<div className="text-center">
<div className="mb-3">
<div>QR code URL:</div>
<ExternalLink className="indivisible" href={qrCodeUrl} />
<CopyToClipboard text={qrCodeUrl}>
<FontAwesomeIcon icon={copyIcon} className="ml-2" />
</CopyToClipboard>
<CopyToClipboardIcon text={qrCodeUrl} />
</div>
<img src={qrCodeUrl} className="qr-code-modal__img" alt="QR code" />
<div className="mt-2">{size}x{size}</div>

View File

@@ -48,11 +48,6 @@
transform: scale(1.5);
}
.short-urls-row__copy-btn {
cursor: pointer;
font-size: 1.2rem;
}
.short-urls-row__copy-hint {
@include vertical-align(translateX(10px));

View File

@@ -2,13 +2,11 @@ import { isEmpty } from 'ramda';
import { FC, useEffect, useRef } from 'react';
import Moment from 'react-moment';
import { ExternalLink } from 'react-external-link';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCopy as copyIcon } from '@fortawesome/free-regular-svg-icons';
import CopyToClipboard from 'react-copy-to-clipboard';
import ColorGenerator from '../../utils/services/ColorGenerator';
import { StateFlagTimeout } from '../../utils/helpers/hooks';
import Tag from '../../tags/helpers/Tag';
import { SelectedServer } from '../../servers/data';
import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon';
import { ShortUrl } from '../data';
import ShortUrlVisitsCount from './ShortUrlVisitsCount';
import { ShortUrlsRowMenuProps } from './ShortUrlsRowMenu';
@@ -60,9 +58,7 @@ const ShortUrlsRow = (
<td className="short-urls-row__cell" data-th="Short URL: ">
<span className="indivisible short-urls-row__cell--relative">
<ExternalLink href={shortUrl.shortUrl} />
<CopyToClipboard text={shortUrl.shortUrl} onCopy={setCopiedToClipboard}>
<FontAwesomeIcon icon={copyIcon} className="ml-2 short-urls-row__copy-btn" />
</CopyToClipboard>
<CopyToClipboardIcon text={shortUrl.shortUrl} onCopy={setCopiedToClipboard} />
<span className="badge badge-warning short-urls-row__copy-hint" hidden={!copiedToClipboard}>
Copied short URL!
</span>