mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Convert feature flags into hooks
This commit is contained in:
parent
bbd8d8ef4e
commit
95439e5602
8 changed files with 41 additions and 26 deletions
|
@ -6,7 +6,7 @@ import { useEffect } from 'react';
|
||||||
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
||||||
import { isReachableServer } from '../servers/data';
|
import { isReachableServer } from '../servers/data';
|
||||||
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
||||||
import { supportsDomainVisits, supportsNonOrphanVisits } from '../utils/helpers/features';
|
import { useFeature } from '../utils/helpers/features';
|
||||||
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
|
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
|
||||||
import type { AsideMenuProps } from './AsideMenu';
|
import type { AsideMenuProps } from './AsideMenu';
|
||||||
import { NotFound } from './NotFound';
|
import { NotFound } from './NotFound';
|
||||||
|
@ -46,8 +46,8 @@ export const MenuLayout = (
|
||||||
return <ServerError />;
|
return <ServerError />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addNonOrphanVisitsRoute = supportsNonOrphanVisits(selectedServer);
|
const addNonOrphanVisitsRoute = useFeature('nonOrphanVisits', selectedServer);
|
||||||
const addDomainVisitsRoute = supportsDomainVisits(selectedServer);
|
const addDomainVisitsRoute = useFeature('domainVisits', selectedServer);
|
||||||
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
||||||
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { DropdownItem } from 'reactstrap';
|
||||||
import type { SelectedServer } from '../../servers/data';
|
import type { SelectedServer } from '../../servers/data';
|
||||||
import { getServerId } from '../../servers/data';
|
import { getServerId } from '../../servers/data';
|
||||||
import { DropdownBtnMenu } from '../../utils/DropdownBtnMenu';
|
import { DropdownBtnMenu } from '../../utils/DropdownBtnMenu';
|
||||||
import { supportsDefaultDomainRedirectsEdition, supportsDomainVisits } from '../../utils/helpers/features';
|
import { useFeature } from '../../utils/helpers/features';
|
||||||
import { useToggle } from '../../utils/helpers/hooks';
|
import { useToggle } from '../../utils/helpers/hooks';
|
||||||
import { DEFAULT_DOMAIN } from '../../visits/reducers/domainVisits';
|
import { DEFAULT_DOMAIN } from '../../visits/reducers/domainVisits';
|
||||||
import type { Domain } from '../data';
|
import type { Domain } from '../data';
|
||||||
|
@ -23,8 +23,8 @@ export const DomainDropdown: FC<DomainDropdownProps> = ({ domain, editDomainRedi
|
||||||
const [isOpen, toggle] = useToggle();
|
const [isOpen, toggle] = useToggle();
|
||||||
const [isModalOpen, toggleModal] = useToggle();
|
const [isModalOpen, toggleModal] = useToggle();
|
||||||
const { isDefault } = domain;
|
const { isDefault } = domain;
|
||||||
const canBeEdited = !isDefault || supportsDefaultDomainRedirectsEdition(selectedServer);
|
const canBeEdited = !isDefault || useFeature('defaultDomainRedirectsEdition', selectedServer);
|
||||||
const withVisits = supportsDomainVisits(selectedServer);
|
const withVisits = useFeature('domainVisits', selectedServer);
|
||||||
const serverId = getServerId(selectedServer);
|
const serverId = getServerId(selectedServer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type { ShortUrlsList as ShortUrlsListState } from '../short-urls/reducers
|
||||||
import { ITEMS_IN_OVERVIEW_PAGE } from '../short-urls/reducers/shortUrlsList';
|
import { ITEMS_IN_OVERVIEW_PAGE } from '../short-urls/reducers/shortUrlsList';
|
||||||
import type { ShortUrlsTableType } from '../short-urls/ShortUrlsTable';
|
import type { ShortUrlsTableType } from '../short-urls/ShortUrlsTable';
|
||||||
import type { TagsList } from '../tags/reducers/tagsList';
|
import type { TagsList } from '../tags/reducers/tagsList';
|
||||||
import { supportsNonOrphanVisits } from '../utils/helpers/features';
|
import { useFeature } from '../utils/helpers/features';
|
||||||
import { prettify } from '../utils/helpers/numbers';
|
import { prettify } from '../utils/helpers/numbers';
|
||||||
import type { VisitsOverview } from '../visits/reducers/visitsOverview';
|
import type { VisitsOverview } from '../visits/reducers/visitsOverview';
|
||||||
import type { SelectedServer } from './data';
|
import type { SelectedServer } from './data';
|
||||||
|
@ -43,7 +43,7 @@ export const Overview = (
|
||||||
const { loading: loadingTags } = tagsList;
|
const { loading: loadingTags } = tagsList;
|
||||||
const { loading: loadingVisits, visitsCount, orphanVisitsCount } = visitsOverview;
|
const { loading: loadingVisits, visitsCount, orphanVisitsCount } = visitsOverview;
|
||||||
const serverId = getServerId(selectedServer);
|
const serverId = getServerId(selectedServer);
|
||||||
const linkToNonOrphanVisits = supportsNonOrphanVisits(selectedServer);
|
const linkToNonOrphanVisits = useFeature('nonOrphanVisits', selectedServer);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { Checkbox } from '../utils/Checkbox';
|
||||||
import type { DateTimeInputProps } from '../utils/dates/DateTimeInput';
|
import type { DateTimeInputProps } from '../utils/dates/DateTimeInput';
|
||||||
import { DateTimeInput } from '../utils/dates/DateTimeInput';
|
import { DateTimeInput } from '../utils/dates/DateTimeInput';
|
||||||
import { formatIsoDate } from '../utils/helpers/date';
|
import { formatIsoDate } from '../utils/helpers/date';
|
||||||
import { supportsForwardQuery } from '../utils/helpers/features';
|
import { useFeature } from '../utils/helpers/features';
|
||||||
import { SimpleCard } from '../utils/SimpleCard';
|
import { SimpleCard } from '../utils/SimpleCard';
|
||||||
import type { OptionalString } from '../utils/utils';
|
import type { OptionalString } from '../utils/utils';
|
||||||
import { handleEventPreventingDefault, hasValue } from '../utils/utils';
|
import { handleEventPreventingDefault, hasValue } from '../utils/utils';
|
||||||
|
@ -116,7 +116,7 @@ export const ShortUrlForm = (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const showForwardQueryControl = supportsForwardQuery(selectedServer);
|
const showForwardQueryControl = useFeature('forwardQuery', selectedServer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form name="shortUrlForm" className="short-url-form" onSubmit={submit}>
|
<form name="shortUrlForm" className="short-url-form" onSubmit={submit}>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { DateRangeSelector } from '../utils/dates/DateRangeSelector';
|
||||||
import { formatIsoDate } from '../utils/helpers/date';
|
import { formatIsoDate } from '../utils/helpers/date';
|
||||||
import type { DateRange } from '../utils/helpers/dateIntervals';
|
import type { DateRange } from '../utils/helpers/dateIntervals';
|
||||||
import { datesToDateRange } from '../utils/helpers/dateIntervals';
|
import { datesToDateRange } from '../utils/helpers/dateIntervals';
|
||||||
import { supportsAllTagsFiltering, supportsFilterDisabledUrls } from '../utils/helpers/features';
|
import { useFeature } from '../utils/helpers/features';
|
||||||
import type { OrderDir } from '../utils/helpers/ordering';
|
import type { OrderDir } from '../utils/helpers/ordering';
|
||||||
import { OrderingDropdown } from '../utils/OrderingDropdown';
|
import { OrderingDropdown } from '../utils/OrderingDropdown';
|
||||||
import { SearchField } from '../utils/SearchField';
|
import { SearchField } from '../utils/SearchField';
|
||||||
|
@ -46,7 +46,7 @@ export const ShortUrlsFilteringBar = (
|
||||||
excludePastValidUntil,
|
excludePastValidUntil,
|
||||||
tagsMode = 'any',
|
tagsMode = 'any',
|
||||||
} = filter;
|
} = filter;
|
||||||
const supportsDisabledFiltering = supportsFilterDisabledUrls(selectedServer);
|
const supportsDisabledFiltering = useFeature('filterDisabledUrls', selectedServer);
|
||||||
|
|
||||||
const setDates = pipe(
|
const setDates = pipe(
|
||||||
({ startDate: theStartDate, endDate: theEndDate }: DateRange) => ({
|
({ startDate: theStartDate, endDate: theEndDate }: DateRange) => ({
|
||||||
|
@ -60,7 +60,7 @@ export const ShortUrlsFilteringBar = (
|
||||||
(searchTerm) => toFirstPage({ search: searchTerm }),
|
(searchTerm) => toFirstPage({ search: searchTerm }),
|
||||||
);
|
);
|
||||||
const changeTagSelection = (selectedTags: string[]) => toFirstPage({ tags: selectedTags });
|
const changeTagSelection = (selectedTags: string[]) => toFirstPage({ tags: selectedTags });
|
||||||
const canChangeTagsMode = supportsAllTagsFiltering(selectedServer);
|
const canChangeTagsMode = useFeature('allTagsFiltering', selectedServer);
|
||||||
const toggleTagsMode = pipe(
|
const toggleTagsMode = pipe(
|
||||||
() => (tagsMode === 'any' ? 'all' : 'any'),
|
() => (tagsMode === 'any' ? 'all' : 'any'),
|
||||||
(mode) => toFirstPage({ tagsMode: mode }),
|
(mode) => toFirstPage({ tagsMode: mode }),
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type { SelectedServer } from '../servers/data';
|
||||||
import { getServerId } from '../servers/data';
|
import { getServerId } from '../servers/data';
|
||||||
import type { Settings } from '../settings/reducers/settings';
|
import type { Settings } from '../settings/reducers/settings';
|
||||||
import { DEFAULT_SHORT_URLS_ORDERING } from '../settings/reducers/settings';
|
import { DEFAULT_SHORT_URLS_ORDERING } from '../settings/reducers/settings';
|
||||||
import { supportsExcludeBotsOnShortUrls } from '../utils/helpers/features';
|
import { useFeature } from '../utils/helpers/features';
|
||||||
import type { OrderDir } from '../utils/helpers/ordering';
|
import type { OrderDir } from '../utils/helpers/ordering';
|
||||||
import { determineOrderDir } from '../utils/helpers/ordering';
|
import { determineOrderDir } from '../utils/helpers/ordering';
|
||||||
import { TableOrderIcon } from '../utils/table/TableOrderIcon';
|
import { TableOrderIcon } from '../utils/table/TableOrderIcon';
|
||||||
|
@ -52,6 +52,7 @@ export const ShortUrlsList = (
|
||||||
);
|
);
|
||||||
const { pagination } = shortUrlsList?.shortUrls ?? {};
|
const { pagination } = shortUrlsList?.shortUrls ?? {};
|
||||||
const doExcludeBots = excludeBots ?? settings.visits?.excludeBots;
|
const doExcludeBots = excludeBots ?? settings.visits?.excludeBots;
|
||||||
|
const supportsExcludingBots = useFeature('excludeBotsOnShortUrls', selectedServer);
|
||||||
const handleOrderBy = (field?: ShortUrlsOrderableFields, dir?: OrderDir) => {
|
const handleOrderBy = (field?: ShortUrlsOrderableFields, dir?: OrderDir) => {
|
||||||
toFirstPage({ orderBy: { field, dir } });
|
toFirstPage({ orderBy: { field, dir } });
|
||||||
setActualOrderBy({ field, dir });
|
setActualOrderBy({ field, dir });
|
||||||
|
@ -65,7 +66,7 @@ export const ShortUrlsList = (
|
||||||
(updatedTags) => toFirstPage({ tags: updatedTags }),
|
(updatedTags) => toFirstPage({ tags: updatedTags }),
|
||||||
);
|
);
|
||||||
const parseOrderByForShlink = ({ field, dir }: ShortUrlsOrder): ShlinkShortUrlsOrder => {
|
const parseOrderByForShlink = ({ field, dir }: ShortUrlsOrder): ShlinkShortUrlsOrder => {
|
||||||
if (supportsExcludeBotsOnShortUrls(selectedServer) && doExcludeBots && field === 'visits') {
|
if (supportsExcludingBots && doExcludeBots && field === 'visits') {
|
||||||
return { field: 'nonBotVisits', dir };
|
return { field: 'nonBotVisits', dir };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Button, FormGroup, Modal, ModalBody, ModalHeader, Row } from 'reactstra
|
||||||
import type { ImageDownloader } from '../../common/services/ImageDownloader';
|
import type { ImageDownloader } from '../../common/services/ImageDownloader';
|
||||||
import type { SelectedServer } from '../../servers/data';
|
import type { SelectedServer } from '../../servers/data';
|
||||||
import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon';
|
import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon';
|
||||||
import { supportsNonRestCors } from '../../utils/helpers/features';
|
import { useFeature } from '../../utils/helpers/features';
|
||||||
import type { QrCodeFormat, QrErrorCorrection } from '../../utils/helpers/qrCodes';
|
import type { QrCodeFormat, QrErrorCorrection } from '../../utils/helpers/qrCodes';
|
||||||
import { buildQrCodeUrl } from '../../utils/helpers/qrCodes';
|
import { buildQrCodeUrl } from '../../utils/helpers/qrCodes';
|
||||||
import type { ShortUrlModalProps } from '../data';
|
import type { ShortUrlModalProps } from '../data';
|
||||||
|
@ -25,7 +25,7 @@ export const QrCodeModal = (imageDownloader: ImageDownloader) => (
|
||||||
const [margin, setMargin] = useState(0);
|
const [margin, setMargin] = useState(0);
|
||||||
const [format, setFormat] = useState<QrCodeFormat>('png');
|
const [format, setFormat] = useState<QrCodeFormat>('png');
|
||||||
const [errorCorrection, setErrorCorrection] = useState<QrErrorCorrection>('L');
|
const [errorCorrection, setErrorCorrection] = useState<QrErrorCorrection>('L');
|
||||||
const displayDownloadBtn = supportsNonRestCors(selectedServer);
|
const displayDownloadBtn = useFeature('nonRestCors', selectedServer);
|
||||||
const qrCodeUrl = useMemo(
|
const qrCodeUrl = useMemo(
|
||||||
() => buildQrCodeUrl(shortUrl, { size, format, margin, errorCorrection }),
|
() => buildQrCodeUrl(shortUrl, { size, format, margin, errorCorrection }),
|
||||||
[shortUrl, size, format, margin, errorCorrection],
|
[shortUrl, size, format, margin, errorCorrection],
|
||||||
|
|
|
@ -1,16 +1,30 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
import type { SelectedServer } from '../../servers/data';
|
import type { SelectedServer } from '../../servers/data';
|
||||||
import { isReachableServer } from '../../servers/data';
|
import { isReachableServer } from '../../servers/data';
|
||||||
|
import { selectServer } from '../../servers/reducers/selectedServer';
|
||||||
import type { SemVerPattern } from './version';
|
import type { SemVerPattern } from './version';
|
||||||
import { versionMatch } from './version';
|
import { versionMatch } from './version';
|
||||||
|
|
||||||
const serverMatchesMinVersion = (minVersion: SemVerPattern) => (selectedServer: SelectedServer): boolean =>
|
const matchesMinVersion = (minVersion: SemVerPattern) => (selectedServer: SelectedServer): boolean =>
|
||||||
isReachableServer(selectedServer) && versionMatch(selectedServer.version, { minVersion });
|
isReachableServer(selectedServer) && versionMatch(selectedServer.version, { minVersion });
|
||||||
|
|
||||||
export const supportsForwardQuery = serverMatchesMinVersion('2.9.0');
|
export const supportedFeatures = {
|
||||||
export const supportsNonRestCors = supportsForwardQuery;
|
forwardQuery: matchesMinVersion('2.9.0'),
|
||||||
export const supportsDefaultDomainRedirectsEdition = serverMatchesMinVersion('2.10.0');
|
nonRestCors: matchesMinVersion('2.9.0'),
|
||||||
export const supportsNonOrphanVisits = serverMatchesMinVersion('3.0.0');
|
defaultDomainRedirectsEdition: matchesMinVersion('2.10.0'),
|
||||||
export const supportsAllTagsFiltering = supportsNonOrphanVisits;
|
nonOrphanVisits: matchesMinVersion('3.0.0'),
|
||||||
export const supportsDomainVisits = serverMatchesMinVersion('3.1.0');
|
allTagsFiltering: matchesMinVersion('3.0.0'),
|
||||||
export const supportsExcludeBotsOnShortUrls = serverMatchesMinVersion('3.4.0');
|
domainVisits: matchesMinVersion('3.1.0'),
|
||||||
export const supportsFilterDisabledUrls = supportsExcludeBotsOnShortUrls;
|
excludeBotsOnShortUrls: matchesMinVersion('3.4.0'),
|
||||||
|
filterDisabledUrls: matchesMinVersion('3.4.0'),
|
||||||
|
deviceLongUrls: matchesMinVersion('3.5.0'),
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
Object.freeze(supportedFeatures);
|
||||||
|
|
||||||
|
type Features = keyof typeof supportedFeatures;
|
||||||
|
|
||||||
|
export const useFeature = (feature: Features, selectedServer: SelectedServer) => useMemo(
|
||||||
|
() => supportedFeatures[feature](selectedServer),
|
||||||
|
[feature, selectServer],
|
||||||
|
);
|
||||||
|
|
Loading…
Reference in a new issue