diff --git a/ui/v2.5/src/components/Settings/PluginPackageManager.tsx b/ui/v2.5/src/components/Settings/PluginPackageManager.tsx index 407aa6e5b45..ed31601397c 100644 --- a/ui/v2.5/src/components/Settings/PluginPackageManager.tsx +++ b/ui/v2.5/src/components/Settings/PluginPackageManager.tsx @@ -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(); 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); diff --git a/ui/v2.5/src/components/Settings/ScraperPackageManager.tsx b/ui/v2.5/src/components/Settings/ScraperPackageManager.tsx index 4d35aef0537..cb6858610b0 100644 --- a/ui/v2.5/src/components/Settings/ScraperPackageManager.tsx +++ b/ui/v2.5/src/components/Settings/ScraperPackageManager.tsx @@ -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(); 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); diff --git a/ui/v2.5/src/components/Shared/PackageManager/PackageManager.tsx b/ui/v2.5/src/components/Shared/PackageManager/PackageManager.tsx index 2c36704cf1a..2a95307160c 100644 --- a/ui/v2.5/src/components/Shared/PackageManager/PackageManager.tsx +++ b/ui/v2.5/src/components/Shared/PackageManager/PackageManager.tsx @@ -64,6 +64,14 @@ function filterPackages(packages: T[], filter: string) { export type InstalledPackage = Omit; +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>; + upgradableOnly: boolean; }> = ({ filter, packages, @@ -132,6 +137,7 @@ const InstalledPackagesList: React.FC<{ updatesLoaded, loading, error, + upgradableOnly, }) => { const checkedMap = useMemo(() => { const map: Record = {}; @@ -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 ( - + ); @@ -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 (
setFilter(v)} /> + {upgradableOnly && ( + + )}
diff --git a/ui/v2.5/src/core/StashService.ts b/ui/v2.5/src/core/StashService.ts index 9dd7054d586..8ff6e388a72 100644 --- a/ui/v2.5/src/core/StashService.ts +++ b/ui/v2.5/src/core/StashService.ts @@ -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) => { diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index bfddf088599..11b6e8b70c4 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -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",