Skip to content

Commit

Permalink
Show upgradable packages only when checking for updates (stashapp#4599)
Browse files Browse the repository at this point in the history
* Sort upgradable packages to top
* Show upgradable packages only by default
* Fix loading state when refetching
  • Loading branch information
WithoutPants committed Feb 20, 2024
1 parent 76e5598 commit 5bb9bf9
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 14 deletions.
5 changes: 4 additions & 1 deletion ui/v2.5/src/components/Settings/PluginPackageManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
mutateUninstallPluginPackages,
mutateUpdatePluginPackages,
pluginMutationImpactedQueries,
isLoading,
} from "src/core/StashService";
import { useMonitorJob } from "src/utils/job";
import {
Expand All @@ -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);

Expand Down
5 changes: 4 additions & 1 deletion ui/v2.5/src/components/Settings/ScraperPackageManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
mutateUninstallScraperPackages,
mutateInstallScraperPackages,
scraperMutationImpactedQueries,
isLoading,
} from "src/core/StashService";
import { useMonitorJob } from "src/utils/job";
import {
Expand All @@ -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);

Expand Down
76 changes: 64 additions & 12 deletions ui/v2.5/src/components/Shared/PackageManager/PackageManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 (
Expand Down Expand Up @@ -124,6 +128,7 @@ const InstalledPackagesList: React.FC<{
packages: InstalledPackage[];
checkedPackages: InstalledPackage[];
setCheckedPackages: React.Dispatch<React.SetStateAction<InstalledPackage[]>>;
upgradableOnly: boolean;
}> = ({
filter,
packages,
Expand All @@ -132,6 +137,7 @@ const InstalledPackagesList: React.FC<{
updatesLoaded,
loading,
error,
upgradableOnly,
}) => {
const checkedMap = useMemo(() => {
const map: Record<string, boolean> = {};
Expand All @@ -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());
Expand Down Expand Up @@ -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>
);
Expand Down Expand Up @@ -242,6 +253,9 @@ const InstalledPackagesToolbar: React.FC<{
onCheckForUpdates: () => void;
onUpdatePackages: () => void;
onUninstallPackages: () => void;

upgradableOnly: boolean;
setUpgradableOnly: (v: boolean) => void;
}> = ({
loading,
checkedPackages,
Expand All @@ -250,15 +264,27 @@ const InstalledPackagesToolbar: React.FC<{
onUninstallPackages,
filter,
setFilter,
upgradableOnly,
setUpgradableOnly,
}) => {
const intl = useIntl();

return (
<div className="package-manager-toolbar">
<ClearableInput
placeholder={`${intl.formatMessage({ id: "filter" })}...`}
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"
Expand Down Expand Up @@ -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) => {
Expand All @@ -330,6 +373,12 @@ export const InstalledPackages: React.FC<{
setUninstalling(false);
}

function checkForUpdates() {
// reset to only show upgradable packages
setUpgradableOnly(true);
onCheckForUpdates();
}

return (
<>
<AlertModal
Expand All @@ -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>
</>
Expand Down
11 changes: 11 additions & 0 deletions ui/v2.5/src/core/StashService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ApolloCache,
DocumentNode,
FetchResult,
NetworkStatus,
useQuery,
} from "@apollo/client";
import { Modifiers } from "@apollo/client/cache";
Expand Down Expand Up @@ -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) => {
Expand Down
1 change: 1 addition & 0 deletions ui/v2.5/src/locales/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 5bb9bf9

Please sign in to comment.