Skip to content

Commit

Permalink
feat: code review and iteration on logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ovflowd committed Dec 25, 2024
1 parent 5b76dc5 commit de26501
Show file tree
Hide file tree
Showing 35 changed files with 492 additions and 492 deletions.
8 changes: 4 additions & 4 deletions apps/site/components/Common/Select/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { Meta as MetaObj, StoryObj } from '@storybook/react';

import Select from '@/components/Common/Select';
import AIX from '@/components/Icons/Platform/AIX';
import Apple from '@/components/Icons/Platform/Apple';
import Linux from '@/components/Icons/Platform/Linux';
import Microsoft from '@/components/Icons/Platform/Microsoft';
import AIX from '@/components/Icons/OperationalSystem/AIX';
import Apple from '@/components/Icons/OperationalSystem/Apple';
import Linux from '@/components/Icons/OperationalSystem/Linux';
import Microsoft from '@/components/Icons/OperationalSystem/Microsoft';

type Story = StoryObj<typeof Select>;
type Meta = MetaObj<typeof Select>;
Expand Down
4 changes: 2 additions & 2 deletions apps/site/components/Common/Select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as ScrollPrimitive from '@radix-ui/react-scroll-area';
import * as SelectPrimitive from '@radix-ui/react-select';
import classNames from 'classnames';
import { useEffect, useId, useMemo, useState } from 'react';
import type { FC } from 'react';
import type { FC, ReactElement } from 'react';

import Skeleton from '@/components/Common/Skeleton';
import type { FormattedMessage } from '@/types';
Expand All @@ -15,7 +15,7 @@ import styles from './index.module.css';
type SelectValue = {
label: FormattedMessage;
value: string;
iconImage?: React.ReactNode;
iconImage?: ReactElement;
disabled?: boolean;
};

Expand Down
7 changes: 6 additions & 1 deletion apps/site/components/Common/Skeleton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import { isValidElement } from 'react';

import styles from './index.module.css';

type SkeletonProps = { loading?: boolean };
type SkeletonProps = { hide?: boolean; loading?: boolean };

const Skeleton: FC<PropsWithChildren<SkeletonProps>> = ({
children,
hide = false,
loading = true,
}) => {
if (!loading && hide) {
return null;
}

if (!loading) {
return children;
}
Expand Down
94 changes: 29 additions & 65 deletions apps/site/components/Downloads/Release/BitnessDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,106 +3,70 @@
import { useTranslations } from 'next-intl';
import type { FC } from 'react';
import { useEffect, useContext, useMemo } from 'react';
import semVer from 'semver';

import Select from '@/components/Common/Select';
import { useClientContext } from '@/hooks';
import { ReleaseContext } from '@/providers/releaseProvider';
import { bitnessItems, formatDropdownItems } from '@/util/downloadUtils';
import { ARCHITECTURES, parseCompatibility } from '@/util/downloadUtils';
import { getUserBitnessByArchitecture } from '@/util/getUserBitnessByArchitecture';

const parseNumericBitness = (bitness: string) =>
/^\d+$/.test(bitness) ? Number(bitness) : bitness;

const BitnessDropdown: FC = () => {
const { bitness: userBitness, architecture: userArchitecture } =
useClientContext();
const { bitness, os, release, setBitness } = useContext(ReleaseContext);
const { architecture, bitness } = useClientContext();

const release = useContext(ReleaseContext);
const t = useTranslations();

useEffect(() => {
setBitness(getUserBitnessByArchitecture(userArchitecture, userBitness));
// we shouldn't update the effect on setter state change
release.setBitness(getUserBitnessByArchitecture(architecture, bitness));
// Only react on the change of the Client Context Architecture and Bitness
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userArchitecture, userBitness]);

// @TODO: We should have a proper utility that gives
// disabled OSs, Platforms, based on specific criteria
// this can be an optimisation for the future
// to remove this logic from this component
const disabledItems = useMemo(() => {
const disabledItems = [];
}, [architecture, bitness]);

if (os === 'WIN' && semVer.satisfies(release.version, '< 19.9.0')) {
disabledItems.push('arm64');
}

if (os === 'WIN' && semVer.satisfies(release.version, '>= 23.0.0')) {
disabledItems.push('86');
}

if (os === 'LINUX' && semVer.satisfies(release.version, '< 4.0.0')) {
disabledItems.push('arm64', 'armv7l');
}

if (os === 'LINUX' && semVer.satisfies(release.version, '< 4.4.0')) {
disabledItems.push('ppc64le');
}

if (os === 'LINUX' && semVer.satisfies(release.version, '< 6.6.0')) {
disabledItems.push('s390x');
}

if (os === 'AIX' && semVer.satisfies(release.version, '< 6.7.0')) {
disabledItems.push('ppc64');
}

return disabledItems;
}, [os, release.version]);
// We parse the compatibility of the dropdown items
const parsedArchitectures = useMemo(
() => parseCompatibility(ARCHITECTURES[release.os], release),
// We only want to react on the change of the OS, Bitness, and Version
// eslint-disable-next-line react-hooks/exhaustive-deps
[release.os, release.bitness, release.version]
);

// @TODO: We should have a proper utility that gives
// disabled OSs, Platforms, based on specific criteria
// this can be an optimisation for the future
// to remove this logic from this component
useEffect(() => {
const mappedBitnessValues = bitnessItems[os].map(({ value }) => value);
const currentBitnessItem = parsedArchitectures.find(
({ value }) => value === String(release.bitness)
);

const currentBitnessExcluded =
// Different OSs support different Bitnessess, hence we should also check
// if besides the current bitness not being supported for a given release version
// we also should check if it is not supported by the OS
disabledItems.includes(String(bitness)) ||
!mappedBitnessValues.includes(String(bitness));
!currentBitnessItem || currentBitnessItem.disabled;

const nonExcludedBitness = mappedBitnessValues.find(
bitness => !disabledItems.includes(bitness)
const nonExcludedBitness = parsedArchitectures.find(
({ disabled }) => !disabled
);

if (currentBitnessExcluded && nonExcludedBitness) {
// We set it as a Number for cases where it is 64 or 86 otherwise we are
// setting it as a string (ARMv7, ARMv6, etc.)
const numericBitness = Number(nonExcludedBitness);

setBitness(
numericBitness.toString() === nonExcludedBitness
release.setBitness(
numericBitness.toString() === nonExcludedBitness.value
? numericBitness
: nonExcludedBitness
: nonExcludedBitness.value
);
}
// we shouldn't react when "actions" change
// Only react on the change of the current OS and Version
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [os, disabledItems]);
}, [release.os, release.version]);

return (
<Select
values={formatDropdownItems({
items: bitnessItems[os],
disabledItems,
})}
loading={os === 'LOADING'}
values={parsedArchitectures}
loading={release.os === 'LOADING'}
ariaLabel={t('layouts.download.dropdown.bitness')}
defaultValue={String(bitness)}
onChange={bitness => setBitness(parseNumericBitness(bitness))}
defaultValue={String(release.bitness)}
onChange={bitness => release.setBitness(parseNumericBitness(bitness))}
className="min-w-28"
inline={true}
/>
Expand Down
72 changes: 32 additions & 40 deletions apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,60 @@
'use client';

import { useTranslations } from 'next-intl';
import { useContext, useEffect } from 'react';
import { useContext, useEffect, useMemo } from 'react';
import type { FC } from 'react';

import Select from '@/components/Common/Select';
import AIX from '@/components/Icons/Platform/AIX';
import Apple from '@/components/Icons/Platform/Apple';
import Linux from '@/components/Icons/Platform/Linux';
import Microsoft from '@/components/Icons/Platform/Microsoft';
import { useClientContext } from '@/hooks';
import { ReleaseContext } from '@/providers/releaseProvider';
import type { UserOS } from '@/types/userOS';
import {
formatDropdownItems,
operatingSystemItems,
} from '@/util/downloadUtils';
import { OPERATING_SYSTEMS, parseCompatibility } from '@/util/downloadUtils';

type OperatingSystemDropdownProps = { exclude?: Array<UserOS> };

const OperatingSystemDropdown: FC<OperatingSystemDropdownProps> = ({
exclude = [],
}) => {
const { os: userOS } = useClientContext();
const { os, setOS } = useContext(ReleaseContext);
const OperatingSystemDropdown: FC<OperatingSystemDropdownProps> = () => {
const { os } = useClientContext();
const release = useContext(ReleaseContext);
const t = useTranslations();

// Reacts on Client Context change of OS
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => setOS(userOS), [userOS]);
useEffect(() => release.setOS(os), [os]);

// We parse the compatibility of the dropdown items
const parsedOperatingSystems = useMemo(
() => parseCompatibility(OPERATING_SYSTEMS, release),
// We only want to react on the change of the OS, Bitness, and Version
// eslint-disable-next-line react-hooks/exhaustive-deps
[release.os, release.bitness, release.version]
);

// @TODO: We should have a proper utility that gives
// disabled OSs, Platforms, based on specific criteria
// this can be an optimisation for the future
// to remove this logic from this component
useEffect(() => {
const currentOSExcluded = exclude.includes(os);
const currentOperatingSystem = parsedOperatingSystems.find(
({ value }) => value === release.os
);

const currentPlatformExcluded =
!currentOperatingSystem || currentOperatingSystem.disabled;

const nonExcludedOS = operatingSystemItems
.map(({ value }) => value)
.find(os => !exclude.includes(os));
const nonExcludedOperatingSystem = parsedOperatingSystems.find(
({ disabled }) => !disabled
);

if (currentOSExcluded && nonExcludedOS) {
setOS(nonExcludedOS);
if (currentPlatformExcluded && nonExcludedOperatingSystem) {
release.setOS(nonExcludedOperatingSystem.value);
}
// we shouldn't react when "actions" change
// Only react on the change of the current Platform
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [os, exclude]);
}, [release.platform]);

return (
<Select
values={formatDropdownItems({
items: operatingSystemItems,
disabledItems: exclude,
icons: {
WIN: <Microsoft width={16} height={16} />,
MAC: <Apple width={16} height={16} />,
LINUX: <Linux width={16} height={16} />,
AIX: <AIX width={16} height={16} />,
},
})}
defaultValue={os}
loading={os === 'LOADING'}
values={parsedOperatingSystems}
defaultValue={release.os}
loading={release.os === 'LOADING'}
ariaLabel={t('layouts.download.dropdown.os')}
onChange={value => setOS(value as UserOS)}
onChange={value => release.setOS(value as UserOS)}
className="min-w-[8.5rem]"
inline={true}
/>
Expand Down
22 changes: 6 additions & 16 deletions apps/site/components/Downloads/Release/PackageManagerDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,21 @@ import { useContext } from 'react';
import type { FC } from 'react';

import Select from '@/components/Common/Select';
import NPM from '@/components/Icons/PackageManager/Npm';
import PNPM from '@/components/Icons/PackageManager/Pnpm';
import YARN from '@/components/Icons/PackageManager/Yarn';
import { ReleaseContext } from '@/providers/releaseProvider';
import type { PackageManager } from '@/types/release';
import { formatDropdownItems, packageManagerItems } from '@/util/downloadUtils';
import { PACKAGE_MANAGERS } from '@/util/downloadUtils';

const PackageManagerDropdown: FC = () => {
const { os, packageManager, setPackageManager } = useContext(ReleaseContext);
const release = useContext(ReleaseContext);
const t = useTranslations();

return (
<Select
values={formatDropdownItems({
items: packageManagerItems,
icons: {
NPM: <NPM width={16} height={16} />,
YARN: <YARN width={16} height={16} />,
PNPM: <PNPM width={16} height={16} />,
},
})}
defaultValue={packageManager}
loading={os === 'LOADING'}
values={PACKAGE_MANAGERS}
defaultValue={release.packageManager}
loading={release.os === 'LOADING' || release.platform === ''}
ariaLabel={t('layouts.download.dropdown.packageManager')}
onChange={manager => setPackageManager(manager as PackageManager)}
onChange={manager => release.setPackageManager(manager as PackageManager)}
className="min-w-28"
inline={true}
/>
Expand Down
Loading

0 comments on commit de26501

Please sign in to comment.