mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 01:20:24 +03:00
Merge pull request #811 from acelaya-forks/feature/feature-flag-hooks
Convert feature flags into hooks
This commit is contained in:
commit
fa69c21fa2
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 { isReachableServer } from '../servers/data';
|
||||
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 type { AsideMenuProps } from './AsideMenu';
|
||||
import { NotFound } from './NotFound';
|
||||
|
@ -46,8 +46,8 @@ export const MenuLayout = (
|
|||
return <ServerError />;
|
||||
}
|
||||
|
||||
const addNonOrphanVisitsRoute = supportsNonOrphanVisits(selectedServer);
|
||||
const addDomainVisitsRoute = supportsDomainVisits(selectedServer);
|
||||
const addNonOrphanVisitsRoute = useFeature('nonOrphanVisits', selectedServer);
|
||||
const addDomainVisitsRoute = useFeature('domainVisits', selectedServer);
|
||||
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
||||
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { DropdownItem } from 'reactstrap';
|
|||
import type { SelectedServer } from '../../servers/data';
|
||||
import { getServerId } from '../../servers/data';
|
||||
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 { DEFAULT_DOMAIN } from '../../visits/reducers/domainVisits';
|
||||
import type { Domain } from '../data';
|
||||
|
@ -23,8 +23,8 @@ export const DomainDropdown: FC<DomainDropdownProps> = ({ domain, editDomainRedi
|
|||
const [isOpen, toggle] = useToggle();
|
||||
const [isModalOpen, toggleModal] = useToggle();
|
||||
const { isDefault } = domain;
|
||||
const canBeEdited = !isDefault || supportsDefaultDomainRedirectsEdition(selectedServer);
|
||||
const withVisits = supportsDomainVisits(selectedServer);
|
||||
const canBeEdited = !isDefault || useFeature('defaultDomainRedirectsEdition', selectedServer);
|
||||
const withVisits = useFeature('domainVisits', selectedServer);
|
||||
const serverId = getServerId(selectedServer);
|
||||
|
||||
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 type { ShortUrlsTableType } from '../short-urls/ShortUrlsTable';
|
||||
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 type { VisitsOverview } from '../visits/reducers/visitsOverview';
|
||||
import type { SelectedServer } from './data';
|
||||
|
@ -43,7 +43,7 @@ export const Overview = (
|
|||
const { loading: loadingTags } = tagsList;
|
||||
const { loading: loadingVisits, visitsCount, orphanVisitsCount } = visitsOverview;
|
||||
const serverId = getServerId(selectedServer);
|
||||
const linkToNonOrphanVisits = supportsNonOrphanVisits(selectedServer);
|
||||
const linkToNonOrphanVisits = useFeature('nonOrphanVisits', selectedServer);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Checkbox } from '../utils/Checkbox';
|
|||
import type { DateTimeInputProps } from '../utils/dates/DateTimeInput';
|
||||
import { DateTimeInput } from '../utils/dates/DateTimeInput';
|
||||
import { formatIsoDate } from '../utils/helpers/date';
|
||||
import { supportsForwardQuery } from '../utils/helpers/features';
|
||||
import { useFeature } from '../utils/helpers/features';
|
||||
import { SimpleCard } from '../utils/SimpleCard';
|
||||
import type { OptionalString } 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 (
|
||||
<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 type { DateRange } 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 { OrderingDropdown } from '../utils/OrderingDropdown';
|
||||
import { SearchField } from '../utils/SearchField';
|
||||
|
@ -46,7 +46,7 @@ export const ShortUrlsFilteringBar = (
|
|||
excludePastValidUntil,
|
||||
tagsMode = 'any',
|
||||
} = filter;
|
||||
const supportsDisabledFiltering = supportsFilterDisabledUrls(selectedServer);
|
||||
const supportsDisabledFiltering = useFeature('filterDisabledUrls', selectedServer);
|
||||
|
||||
const setDates = pipe(
|
||||
({ startDate: theStartDate, endDate: theEndDate }: DateRange) => ({
|
||||
|
@ -60,7 +60,7 @@ export const ShortUrlsFilteringBar = (
|
|||
(searchTerm) => toFirstPage({ search: searchTerm }),
|
||||
);
|
||||
const changeTagSelection = (selectedTags: string[]) => toFirstPage({ tags: selectedTags });
|
||||
const canChangeTagsMode = supportsAllTagsFiltering(selectedServer);
|
||||
const canChangeTagsMode = useFeature('allTagsFiltering', selectedServer);
|
||||
const toggleTagsMode = pipe(
|
||||
() => (tagsMode === 'any' ? 'all' : 'any'),
|
||||
(mode) => toFirstPage({ tagsMode: mode }),
|
||||
|
|
|
@ -9,7 +9,7 @@ import type { SelectedServer } from '../servers/data';
|
|||
import { getServerId } from '../servers/data';
|
||||
import type { Settings } 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 { determineOrderDir } from '../utils/helpers/ordering';
|
||||
import { TableOrderIcon } from '../utils/table/TableOrderIcon';
|
||||
|
@ -52,6 +52,7 @@ export const ShortUrlsList = (
|
|||
);
|
||||
const { pagination } = shortUrlsList?.shortUrls ?? {};
|
||||
const doExcludeBots = excludeBots ?? settings.visits?.excludeBots;
|
||||
const supportsExcludingBots = useFeature('excludeBotsOnShortUrls', selectedServer);
|
||||
const handleOrderBy = (field?: ShortUrlsOrderableFields, dir?: OrderDir) => {
|
||||
toFirstPage({ orderBy: { field, dir } });
|
||||
setActualOrderBy({ field, dir });
|
||||
|
@ -65,7 +66,7 @@ export const ShortUrlsList = (
|
|||
(updatedTags) => toFirstPage({ tags: updatedTags }),
|
||||
);
|
||||
const parseOrderByForShlink = ({ field, dir }: ShortUrlsOrder): ShlinkShortUrlsOrder => {
|
||||
if (supportsExcludeBotsOnShortUrls(selectedServer) && doExcludeBots && field === 'visits') {
|
||||
if (supportsExcludingBots && doExcludeBots && field === 'visits') {
|
||||
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 { SelectedServer } from '../../servers/data';
|
||||
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 { buildQrCodeUrl } from '../../utils/helpers/qrCodes';
|
||||
import type { ShortUrlModalProps } from '../data';
|
||||
|
@ -25,7 +25,7 @@ export const QrCodeModal = (imageDownloader: ImageDownloader) => (
|
|||
const [margin, setMargin] = useState(0);
|
||||
const [format, setFormat] = useState<QrCodeFormat>('png');
|
||||
const [errorCorrection, setErrorCorrection] = useState<QrErrorCorrection>('L');
|
||||
const displayDownloadBtn = supportsNonRestCors(selectedServer);
|
||||
const displayDownloadBtn = useFeature('nonRestCors', selectedServer);
|
||||
const qrCodeUrl = useMemo(
|
||||
() => buildQrCodeUrl(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 { isReachableServer } from '../../servers/data';
|
||||
import { selectServer } from '../../servers/reducers/selectedServer';
|
||||
import type { SemVerPattern } 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 });
|
||||
|
||||
export const supportsForwardQuery = serverMatchesMinVersion('2.9.0');
|
||||
export const supportsNonRestCors = supportsForwardQuery;
|
||||
export const supportsDefaultDomainRedirectsEdition = serverMatchesMinVersion('2.10.0');
|
||||
export const supportsNonOrphanVisits = serverMatchesMinVersion('3.0.0');
|
||||
export const supportsAllTagsFiltering = supportsNonOrphanVisits;
|
||||
export const supportsDomainVisits = serverMatchesMinVersion('3.1.0');
|
||||
export const supportsExcludeBotsOnShortUrls = serverMatchesMinVersion('3.4.0');
|
||||
export const supportsFilterDisabledUrls = supportsExcludeBotsOnShortUrls;
|
||||
export const supportedFeatures = {
|
||||
forwardQuery: matchesMinVersion('2.9.0'),
|
||||
nonRestCors: matchesMinVersion('2.9.0'),
|
||||
defaultDomainRedirectsEdition: matchesMinVersion('2.10.0'),
|
||||
nonOrphanVisits: matchesMinVersion('3.0.0'),
|
||||
allTagsFiltering: matchesMinVersion('3.0.0'),
|
||||
domainVisits: matchesMinVersion('3.1.0'),
|
||||
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