import React, { useState } from "react";
import {
Dialog, DialogTitle, DialogContent, DialogActions, Button,
Grid, Typography, Link, Box, Tab, Tabs, IconButton, Chip, FormControlLabel, Checkbox,
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import DeleteIcon from "@mui/icons-material/Delete";
import RefreshIcon from "@mui/icons-material/Refresh";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import BuildLogsTab from "components/package/BuildLogsTab";
import ChangesTab from "components/package/ChangesTab";
import EventsTab from "components/package/EventsTab";
import AutoRefreshControl from "components/common/AutoRefreshControl";
import { useRepository } from "hooks/useRepository";
import { useAuth } from "hooks/useAuth";
import { useNotification } from "hooks/useNotification";
import { useAutoRefresh } from "hooks/useAutoRefresh";
import { Client } from "api/client/AhrimanClient";
import { ApiError } from "api/client/ApiError";
import { QueryKeys } from "api/QueryKeys";
import { StatusHeaderStyles } from "theme/status/StatusHeaderStyles";
import { formatTimestamp } from "components/common/formatTimestamp";
import type { AutoRefreshInterval } from "api/types/AutoRefreshInterval";
import type { Dependencies } from "api/types/Dependencies";
import type { PackageProperties } from "api/types/PackageProperties";
import type { PackageStatus } from "api/types/PackageStatus";
import type { Patch } from "api/types/Patch";
import type { RepositoryId } from "api/types/RepositoryId";
interface PackageInfoDialogProps {
packageBase: string | null;
open: boolean;
onClose: () => void;
autorefreshIntervals: AutoRefreshInterval[];
}
function listToString(items: string[]): React.ReactNode {
const unique = [...new Set(items)].sort();
return unique.map((item, i) => (
{item}
{i < unique.length - 1 &&
}
));
}
export default function PackageInfoDialog({ packageBase, open, onClose, autorefreshIntervals }: PackageInfoDialogProps): React.JSX.Element {
const { current } = useRepository();
const { enabled: authEnabled, username } = useAuth();
const { showSuccess, showError } = useNotification();
const queryClient = useQueryClient();
const hasAuth = !authEnabled || username !== null;
const [tabIndex, setTabIndex] = useState(0);
const [refreshDb, setRefreshDb] = useState(true);
const defaultInterval = autorefreshIntervals.find((i) => i.is_active)?.interval ?? 0;
const autoRefresh = useAutoRefresh("package-info-autoreload-button", defaultInterval);
const repo = current as RepositoryId;
const { data: packageData } = useQuery({
queryKey: packageBase && repo ? QueryKeys.package(packageBase, repo) : ["package-none"],
queryFn: () => Client.fetchPackage(packageBase!, repo),
enabled: !!packageBase && !!repo && open,
refetchInterval: autoRefresh.refetchInterval,
});
const { data: dependencies } = useQuery({
queryKey: packageBase && repo ? QueryKeys.dependencies(packageBase, repo) : ["deps-none"],
queryFn: () => Client.fetchDependencies(packageBase!, repo),
enabled: !!packageBase && !!repo && open,
});
const { data: patches = [] } = useQuery({
queryKey: packageBase ? QueryKeys.patches(packageBase) : ["patches-none"],
queryFn: () => Client.fetchPatches(packageBase!),
enabled: !!packageBase && open,
});
const description: PackageStatus | undefined = packageData?.[0];
const pkg = description?.package;
const status = description?.status;
const headerStyle = status ? StatusHeaderStyles[status.status] : {};
// Flatten depends from all sub-packages
const allDepends: string[] = pkg
? Object.values(pkg.packages).flatMap((p: PackageProperties) => {
const pkgNames = Object.keys(pkg.packages);
const deps = (p.depends ?? []).filter((d: string) => !pkgNames.includes(d));
const makeDeps = (p.make_depends ?? []).filter((d: string) => !pkgNames.includes(d)).map((d: string) => `${d} (make)`);
const optDeps = (p.opt_depends ?? []).filter((d: string) => !pkgNames.includes(d)).map((d: string) => `${d} (optional)`);
return [...deps, ...makeDeps, ...optDeps];
})
: [];
const implicitDepends: string[] = dependencies
? Object.values(dependencies.paths).flat()
: [];
const groups: string[] = pkg
? Object.values(pkg.packages).flatMap((p: PackageProperties) => p.groups ?? [])
: [];
const licenses: string[] = pkg
? Object.values(pkg.packages).flatMap((p: PackageProperties) => p.licenses ?? [])
: [];
const upstreamUrls: string[] = pkg
? [...new Set(Object.values(pkg.packages).map((p: PackageProperties) => p.url).filter((u): u is string => !!u))].sort()
: [];
const aurUrl = pkg?.remote.web_url;
const packagesList: string[] = pkg
? Object.entries(pkg.packages).map(([name, p]) => `${name}${p.description ? ` (${p.description})` : ""}`)
: [];
const handleUpdate = async (): Promise => {
if (!packageBase || !repo) {
return;
}
try {
await Client.addPackages(repo, { packages: [packageBase], refresh: refreshDb });
showSuccess("Success", `Run update for packages ${packageBase}`);
} catch (e) {
const detail = e instanceof ApiError ? e.detail : String(e);
showError("Action failed", `Package update failed: ${detail}`);
}
};
const handleRemove = async (): Promise => {
if (!packageBase || !repo) {
return;
}
try {
await Client.removePackages(repo, [packageBase]);
showSuccess("Success", `Packages ${packageBase} have been removed`);
onClose();
} catch (e) {
const detail = e instanceof ApiError ? e.detail : String(e);
showError("Action failed", `Could not remove package: ${detail}`);
}
};
const handleDeletePatch = async (key: string): Promise => {
if (!packageBase) {
return;
}
try {
await Client.deletePatch(packageBase, key);
void queryClient.invalidateQueries({ queryKey: QueryKeys.patches(packageBase) });
} catch (e) {
const detail = e instanceof ApiError ? e.detail : String(e);
showError("Action failed", `Could not delete variable: ${detail}`);
}
};
const handleReload = (): void => {
if (!packageBase || !repo) {
return;
}
void queryClient.invalidateQueries({ queryKey: QueryKeys.package(packageBase, repo) });
void queryClient.invalidateQueries({ queryKey: QueryKeys.logs(packageBase, repo) });
void queryClient.invalidateQueries({ queryKey: QueryKeys.changes(packageBase, repo) });
void queryClient.invalidateQueries({ queryKey: QueryKeys.events(repo, packageBase) });
void queryClient.invalidateQueries({ queryKey: QueryKeys.dependencies(packageBase, repo) });
void queryClient.invalidateQueries({ queryKey: QueryKeys.patches(packageBase) });
};
const handleClose = (): void => {
setTabIndex(0);
setRefreshDb(true);
onClose();
};
return (
);
}