import React, { FunctionComponent, ReactNode, useCallback, useEffect, useState } from 'react'; import { Box, Button, Chip, CircularProgress, FormControl, Grid, IconButton, Input, InputAdornment, InputLabel, Link, makeStyles, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, } from '@material-ui/core'; import { Launch, CloudDownload, Add } from '@material-ui/icons'; import axios from 'axios'; import { isEqual } from 'lodash'; import { useTranslation } from 'react-i18next'; import { DateTime } from 'luxon'; import { ProfilesEntity } from '../../../types/overview'; import { Profile, TitlesEntity } from '../../../types/profile'; import config from '../../../config'; import { getTitle } from '../utils/title'; import asu from '../../../utils/asu'; import { GetBuildResponse } from '../../../types/asu'; const useStyles = makeStyles(() => ({ chip: { '&:focus': { border: `2px solid #000`, }, }, })); type Props = { selectedVersion: string; selectedProfile: ProfilesEntity; }; const profilesData: { [key: string]: Profile } = {}; const ProfileDetails: FunctionComponent = ({ selectedVersion, selectedProfile }) => { const classes = useStyles(); const [profile, setProfile] = useState(); const [showAddPackages, setShowAddPackages] = useState(false); const [customPackages, setCustomPackages] = useState>(new Set()); const [customPackagesInputValue, setCustomPackagesInputValue] = useState(''); const [showPackageExistsError, setShowPackageExistsError] = useState(false); const [buildStatus, setBuildStatus] = useState(); const [buildResponse, setBuildResponse] = useState(); const [buildError, setBuildError] = useState(); const { t } = useTranslation(); const getHelpKey = (type: string) => { const lc = type.toLowerCase(); if (lc.includes('sysupgrade')) { return 'sysupgrade-help'; } if (lc.includes('factory') || lc === 'trx' || lc === 'chk') { return 'factory-help'; } if (lc.includes('kernel') || lc.includes('zimage') || lc.includes('uimage')) { return 'kernel-help'; } if (lc.includes('root')) { return 'rootfs-help'; } if (lc.includes('sdcard')) { return 'sdcard-help'; } if (lc.includes('tftp')) { return 'tftp-help'; } return 'other-help'; }; const preExistingPackages = (_profile = profile) => Array.from( new Set([...(_profile?.default_packages || []), ...(_profile?.device_packages || [])]) ); const getProfileData = useCallback(async () => { let profileData = profilesData[selectedProfile.id]; if (!profileData) { const response = await axios.get( `${process.env.PUBLIC_URL}/data/${selectedVersion}` + `/${selectedProfile.target}/${selectedProfile.id}.json` ); profileData = response.data; profilesData[selectedProfile.id] = profileData; } return profileData; }, [selectedVersion, selectedProfile]); useEffect(() => { let mounted = true; getProfileData().then((_profileData) => { if (mounted && !isEqual(profile, _profileData)) { setProfile(_profileData); setCustomPackages(new Set(preExistingPackages(_profileData))); } }); return () => { mounted = false; }; }, [selectedVersion, selectedProfile, getProfileData, profile]); const toggleAddPackages = () => { if (!profile) return; setShowAddPackages(!showAddPackages); setCustomPackages(new Set(preExistingPackages())); }; const appendAddPackageInput = ( e?: React.KeyboardEvent ) => { if (((e && e.key === 'Enter') || !e) && customPackagesInputValue) { setCustomPackages((prev) => { setShowPackageExistsError(false); if ( !profile?.device_packages?.includes(customPackagesInputValue) && !profile?.default_packages?.includes(customPackagesInputValue) ) { return new Set(prev.add(customPackagesInputValue)); } setShowPackageExistsError(true); return prev; }); setCustomPackagesInputValue(''); } }; const onBuildStatusChange = (status: string) => { setBuildStatus(status); }; if (!profile) return ; const buildCustomImage = async () => { setBuildStatus('Please wait...'); try { const response = await asu.build( Array.from(customPackages.values()), profile.id, profile.version_number, onBuildStatusChange ); setBuildResponse(response); } catch (e) { console.log(e); setBuildError(e.response.data.message); } setBuildStatus(undefined); }; const buildAt = DateTime.fromFormat(profile.build_at, 'yyyy-MM-dd TT').toLocaleString( DateTime.DATETIME_MED ); const hasAbilityToBuildCustomPackages = Object.keys(config).includes('asu_url'); const canBuild = !isEqual(Array.from(customPackages.values()), preExistingPackages()); return ( <> {t('tr-version-build')} {t('tr-model')} {profile.titles?.map((title) => getTitle(title)).join(', ')} {t('tr-target')} {profile.target} {t('tr-version')} {profile.version_number} ({profile.version_code}) {t('tr-date')} {buildAt.toLocaleString()} Info {profile.titles ?.map((title: TitlesEntity) => { const titleString = getTitle(title); const infoUrl = config.info_url.replace('{title}', encodeURI(titleString)); return ( {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} {profile.titles!.length > 1 && ( {titleString} )} ); }) .reduce((acc: ReactNode, curr: ReactNode, i: number) => [ acc, // eslint-disable-next-line react/no-array-index-key , curr, ])}
{t('tr-downloads')} Download link Help Text {profile.images?.map((i) => { const downloadURL = `${config.image_url .replace('{target}', profile.target) .replace('{version}', profile.version_number)}/${i.name}`; return ( {t(`tr-${getHelpKey(i.type)}`)} sha256sum: {i.sha256} ); })}
{t('Packages')} {profile.default_packages && profile.default_packages.length > 0 && ( {t('Default Packages')} {profile.default_packages?.join(', ')} )} {profile.device_packages && profile.device_packages.length > 0 && ( {t('Device Packages')} {profile.device_packages.join(', ')} )} {hasAbilityToBuildCustomPackages && ( {!showAddPackages && ( )} {showAddPackages && ( <> {t('Customize Packages')} find packages index on this page
{Array.from(customPackages.values()).map((p) => { const isDefaultPackage = profile.default_packages?.includes(p); return ( setCustomPackages((prev) => { const newSet = new Set(Array.from(prev.values())); newSet.delete(p); return newSet; }) } /> ); })}
Custom Package Name e && setCustomPackagesInputValue(e.currentTarget.value)} onKeyUp={appendAddPackageInput} endAdornment={ appendAddPackageInput()}> } /> {showPackageExistsError && ( This profile already includes this package. Please try a diffrent one )} {!buildStatus && ( )} {buildStatus && ( <> {typeof buildStatus === 'string' && ( )} {buildStatus} )} )} {buildError && {buildError}} {buildResponse && ( Built Image: Build At {DateTime.fromFormat( buildResponse.build_at.substr(0, 25), 'ccc, dd MMM yyyy TT', { zone: buildResponse.build_at.substr(26), setZone: true, } ).toLocaleString(DateTime.DATETIME_MED)}


Download link Help Text {buildResponse.images?.map((i) => { const downloadURL = `${config.image_url .replace('{target}', profile.target) .replace('{version}', profile.version_number)}/${i.name}`; return ( {t(`tr-${getHelpKey(i.type)}`)} sha256sum: {i.sha256} ); })}
)}
)} ); }; export default ProfileDetails;