mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Show upgradable packages only when checking for updates (#4599)
* Sort upgradable packages to top * Show upgradable packages only by default * Fix loading state when refetching
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
mutateUninstallPluginPackages,
|
||||
mutateUpdatePluginPackages,
|
||||
pluginMutationImpactedQueries,
|
||||
isLoading,
|
||||
} from "src/core/StashService";
|
||||
import { useMonitorJob } from "src/utils/job";
|
||||
import {
|
||||
@@ -25,9 +26,11 @@ export const InstalledPluginPackages: React.FC = () => {
|
||||
const [jobID, setJobID] = useState<string>();
|
||||
const { job } = useMonitorJob(jobID, () => onPackageChanges());
|
||||
|
||||
const { data, previousData, refetch, loading, error } =
|
||||
const { data, previousData, refetch, networkStatus, error } =
|
||||
useInstalledPluginPackages(loadUpgrades);
|
||||
|
||||
const loading = isLoading(networkStatus);
|
||||
|
||||
async function onUpdatePackages(packages: GQL.PackageSpecInput[]) {
|
||||
const r = await mutateUpdatePluginPackages(packages);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
mutateUninstallScraperPackages,
|
||||
mutateInstallScraperPackages,
|
||||
scraperMutationImpactedQueries,
|
||||
isLoading,
|
||||
} from "src/core/StashService";
|
||||
import { useMonitorJob } from "src/utils/job";
|
||||
import {
|
||||
@@ -25,9 +26,11 @@ export const InstalledScraperPackages: React.FC = () => {
|
||||
const [jobID, setJobID] = useState<string>();
|
||||
const { job } = useMonitorJob(jobID, () => onPackageChanges());
|
||||
|
||||
const { data, previousData, refetch, loading, error } =
|
||||
const { data, previousData, refetch, networkStatus, error } =
|
||||
useInstalledScraperPackages(loadUpgrades);
|
||||
|
||||
const loading = isLoading(networkStatus);
|
||||
|
||||
async function onUpdatePackages(packages: GQL.PackageSpecInput[]) {
|
||||
const r = await mutateUpdateScraperPackages(packages);
|
||||
|
||||
|
||||
@@ -64,6 +64,14 @@ function filterPackages<T extends IPackage>(packages: T[], filter: string) {
|
||||
|
||||
export type InstalledPackage = Omit<GQL.Package, "requires">;
|
||||
|
||||
function hasUpgrade(pkg: InstalledPackage) {
|
||||
if (!pkg.date || !pkg.source_package?.date) return false;
|
||||
|
||||
const pkgDate = new Date(pkg.date);
|
||||
const upgradeDate = new Date(pkg.source_package.date);
|
||||
return upgradeDate > pkgDate;
|
||||
}
|
||||
|
||||
const InstalledPackageRow: React.FC<{
|
||||
loading?: boolean;
|
||||
pkg: InstalledPackage;
|
||||
@@ -75,11 +83,7 @@ const InstalledPackageRow: React.FC<{
|
||||
|
||||
const updateAvailable = useMemo(() => {
|
||||
if (!updatesLoaded) return false;
|
||||
if (!pkg.date || !pkg.source_package?.date) return false;
|
||||
|
||||
const pkgDate = new Date(pkg.date);
|
||||
const upgradeDate = new Date(pkg.source_package.date);
|
||||
return upgradeDate > pkgDate;
|
||||
return hasUpgrade(pkg);
|
||||
}, [updatesLoaded, pkg]);
|
||||
|
||||
return (
|
||||
@@ -124,6 +128,7 @@ const InstalledPackagesList: React.FC<{
|
||||
packages: InstalledPackage[];
|
||||
checkedPackages: InstalledPackage[];
|
||||
setCheckedPackages: React.Dispatch<React.SetStateAction<InstalledPackage[]>>;
|
||||
upgradableOnly: boolean;
|
||||
}> = ({
|
||||
filter,
|
||||
packages,
|
||||
@@ -132,6 +137,7 @@ const InstalledPackagesList: React.FC<{
|
||||
updatesLoaded,
|
||||
loading,
|
||||
error,
|
||||
upgradableOnly,
|
||||
}) => {
|
||||
const checkedMap = useMemo(() => {
|
||||
const map: Record<string, boolean> = {};
|
||||
@@ -146,8 +152,10 @@ const InstalledPackagesList: React.FC<{
|
||||
}, [checkedPackages, packages]);
|
||||
|
||||
const filteredPackages = useMemo(() => {
|
||||
return filterPackages(packages, filter);
|
||||
}, [filter, packages]);
|
||||
return filterPackages(packages, filter).filter((pkg) => {
|
||||
return !updatesLoaded || !upgradableOnly || hasUpgrade(pkg);
|
||||
});
|
||||
}, [packages, filter, updatesLoaded, upgradableOnly]);
|
||||
|
||||
function toggleAllChecked() {
|
||||
setCheckedPackages(allChecked ? [] : packages.slice());
|
||||
@@ -179,10 +187,13 @@ const InstalledPackagesList: React.FC<{
|
||||
}
|
||||
|
||||
if (filteredPackages.length === 0) {
|
||||
const id = upgradableOnly
|
||||
? "package_manager.no_upgradable"
|
||||
: "package_manager.no_packages";
|
||||
return (
|
||||
<tr className="package-manager-no-results">
|
||||
<td colSpan={1000}>
|
||||
<FormattedMessage id="package_manager.no_packages" />
|
||||
<FormattedMessage id={id} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
@@ -242,6 +253,9 @@ const InstalledPackagesToolbar: React.FC<{
|
||||
onCheckForUpdates: () => void;
|
||||
onUpdatePackages: () => void;
|
||||
onUninstallPackages: () => void;
|
||||
|
||||
upgradableOnly: boolean;
|
||||
setUpgradableOnly: (v: boolean) => void;
|
||||
}> = ({
|
||||
loading,
|
||||
checkedPackages,
|
||||
@@ -250,8 +264,11 @@ const InstalledPackagesToolbar: React.FC<{
|
||||
onUninstallPackages,
|
||||
filter,
|
||||
setFilter,
|
||||
upgradableOnly,
|
||||
setUpgradableOnly,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<div className="package-manager-toolbar">
|
||||
<ClearableInput
|
||||
@@ -259,6 +276,15 @@ const InstalledPackagesToolbar: React.FC<{
|
||||
value={filter}
|
||||
setValue={(v) => setFilter(v)}
|
||||
/>
|
||||
{upgradableOnly && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="primary"
|
||||
onClick={() => setUpgradableOnly(!upgradableOnly)}
|
||||
>
|
||||
<FormattedMessage id="package_manager.show_all" />
|
||||
</Button>
|
||||
)}
|
||||
<div className="flex-grow-1" />
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -306,11 +332,28 @@ export const InstalledPackages: React.FC<{
|
||||
[]
|
||||
);
|
||||
const [filter, setFilter] = useState("");
|
||||
const [upgradableOnly, setUpgradableOnly] = useState(true);
|
||||
const [uninstalling, setUninstalling] = useState(false);
|
||||
|
||||
// sort packages so that those with updates are at the top
|
||||
const sortedPackages = useMemo(() => {
|
||||
return packages.slice().sort((a, b) => {
|
||||
const aHasUpdate = hasUpgrade(a);
|
||||
const bHasUpdate = hasUpgrade(b);
|
||||
|
||||
if (aHasUpdate && !bHasUpdate) return -1;
|
||||
if (!aHasUpdate && bHasUpdate) return 1;
|
||||
|
||||
// sort by name
|
||||
return a.package_id.localeCompare(b.package_id);
|
||||
});
|
||||
}, [packages]);
|
||||
|
||||
const filteredPackages = useMemo(() => {
|
||||
return filterPackages(checkedPackages, filter);
|
||||
}, [checkedPackages, filter]);
|
||||
return filterPackages(checkedPackages, filter).filter((pkg) => {
|
||||
return !updatesLoaded || !upgradableOnly || hasUpgrade(pkg);
|
||||
});
|
||||
}, [checkedPackages, filter, updatesLoaded, upgradableOnly]);
|
||||
|
||||
useEffect(() => {
|
||||
setCheckedPackages((prev) => {
|
||||
@@ -330,6 +373,12 @@ export const InstalledPackages: React.FC<{
|
||||
setUninstalling(false);
|
||||
}
|
||||
|
||||
function checkForUpdates() {
|
||||
// reset to only show upgradable packages
|
||||
setUpgradableOnly(true);
|
||||
onCheckForUpdates();
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AlertModal
|
||||
@@ -349,19 +398,22 @@ export const InstalledPackages: React.FC<{
|
||||
setFilter={(f) => setFilter(f)}
|
||||
loading={loading}
|
||||
checkedPackages={filteredPackages}
|
||||
onCheckForUpdates={onCheckForUpdates}
|
||||
onCheckForUpdates={() => checkForUpdates()}
|
||||
onUpdatePackages={() => onUpdatePackages(filteredPackages)}
|
||||
onUninstallPackages={() => setUninstalling(true)}
|
||||
upgradableOnly={updatesLoaded && upgradableOnly}
|
||||
setUpgradableOnly={(v) => setUpgradableOnly(v)}
|
||||
/>
|
||||
<InstalledPackagesList
|
||||
filter={filter}
|
||||
loading={loading}
|
||||
error={error}
|
||||
packages={packages}
|
||||
packages={sortedPackages}
|
||||
// use original checked packages so that check boxes are not affected by filter
|
||||
checkedPackages={checkedPackages}
|
||||
setCheckedPackages={setCheckedPackages}
|
||||
updatesLoaded={updatesLoaded}
|
||||
upgradableOnly={upgradableOnly}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
ApolloCache,
|
||||
DocumentNode,
|
||||
FetchResult,
|
||||
NetworkStatus,
|
||||
useQuery,
|
||||
} from "@apollo/client";
|
||||
import { Modifiers } from "@apollo/client/cache";
|
||||
@@ -93,6 +94,16 @@ function deleteObject(
|
||||
cache.evict({ id: cache.identify(obj) });
|
||||
}
|
||||
|
||||
export function isLoading(networkStatus: NetworkStatus) {
|
||||
// useQuery hook loading field only returns true when initially loading the query
|
||||
// and not during subsequent fetches
|
||||
return (
|
||||
networkStatus === NetworkStatus.loading ||
|
||||
networkStatus === NetworkStatus.fetchMore ||
|
||||
networkStatus === NetworkStatus.refetch
|
||||
);
|
||||
}
|
||||
|
||||
/// Object queries
|
||||
|
||||
export const useFindScene = (id: string) => {
|
||||
|
||||
@@ -1095,6 +1095,7 @@
|
||||
"latest_version": "Latest Version",
|
||||
"no_packages": "No packages found",
|
||||
"no_sources": "No sources configured",
|
||||
"no_upgradable": "No upgradable packages found",
|
||||
"package": "Package",
|
||||
"required_by": "Required by {packages}",
|
||||
"selected_only": "Selected only",
|
||||
|
||||
Reference in New Issue
Block a user