mirror of
https://github.com/ivabus/gui
synced 2025-04-23 14:07:14 +03:00
Show UPDATED status (#658)
This commit is contained in:
parent
543411433b
commit
2f133f4b77
13 changed files with 98 additions and 35 deletions
|
@ -4,6 +4,7 @@
|
|||
import { afterUpdate } from "svelte";
|
||||
import { packagesStore } from "$libs/stores";
|
||||
import Button from "@tea/ui/button/button.svelte";
|
||||
import { packageHadError, packageWasInstalled } from "$libs/packages/pkg-utils";
|
||||
|
||||
let root: HTMLElement | undefined;
|
||||
|
||||
|
@ -28,7 +29,7 @@
|
|||
const myConfetti = confetti.create(canvas, { resize: true });
|
||||
await myConfetti({ particleCount: 500, spread: 360, startVelocity: 20, gravity: 0.5 });
|
||||
|
||||
root.removeChild(canvas);
|
||||
root?.removeChild(canvas);
|
||||
isAnimating = false;
|
||||
}
|
||||
};
|
||||
|
@ -39,14 +40,14 @@
|
|||
};
|
||||
|
||||
afterUpdate(() => {
|
||||
if (pkg.displayState?.kind === "INSTALLED_SUCCESSFULLY") {
|
||||
if (packageWasInstalled(pkg)) {
|
||||
playConfetti();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={root} class="z-60 pointer-events-none absolute left-0 top-0 h-full w-full">
|
||||
{#if pkg.displayState?.kind === "INSTALLATION_ERROR"}
|
||||
{#if packageHadError(pkg)}
|
||||
<div
|
||||
class="pointer-events-auto flex h-full w-full flex-col items-center justify-center bg-black/90"
|
||||
>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import "../../app.css";
|
||||
import ProgressCircle from "@tea/ui/progress-circle/progress-circle.svelte";
|
||||
import { PackageStates, type GUIPackage } from "$libs/types";
|
||||
import { findRecentInstalledVersion } from "$libs/packages/pkg-utils";
|
||||
import { findRecentInstalledVersion, packageWasUpdated } from "$libs/packages/pkg-utils";
|
||||
import BgImage from "./bg-image.svelte";
|
||||
import PackageInstallButton from "$components/package-install-button/package-install-button.svelte";
|
||||
import PackageInstalledBadge from "$components/package-install-button/package-installed-badge.svelte";
|
||||
|
@ -28,7 +28,11 @@
|
|||
</script>
|
||||
|
||||
<div class="relative">
|
||||
<section class="package-card relative h-auto border border-gray {layout}" class:active={isActive}>
|
||||
<section
|
||||
class="package-card relative h-auto border border-gray {layout}"
|
||||
class:active={isActive}
|
||||
class:updated={packageWasUpdated(pkg)}
|
||||
>
|
||||
<BgImage class="absolute left-0 top-0 h-full w-full" {layout} {pkg} />
|
||||
|
||||
<a href={link} on:mousedown={activate} on:mouseup={deactivate} on:mouseleave={deactivate}>
|
||||
|
@ -43,16 +47,26 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="content-container absolute bottom-0 w-full {layout}">
|
||||
<article class="card-thumb-label relative">
|
||||
<article class="card-thumb-label">
|
||||
{#if layout === "bottom"}
|
||||
<div class="flex items-center">
|
||||
<h3 class="text-bold font-mona text-2xl font-bold text-white line-clamp-1">
|
||||
{fixPackageName(pkg.name)}
|
||||
</h3>
|
||||
{#if pkg.state === PackageStates.INSTALLED}
|
||||
<i class="icon-check-circle-o mb-1 ml-2 flex text-2xl text-[#00ffd0]" />
|
||||
{/if}
|
||||
</div>
|
||||
<p class="h-[32px] text-xs font-thin lowercase line-clamp-2">{pkg.desc ?? ""}</p>
|
||||
{:else}
|
||||
<h3 class="text-bold mb-4 font-mona text-3xl font-bold text-white line-clamp-1">
|
||||
<div class="mb-4 flex items-center">
|
||||
<h3 class="text-bold font-mona text-3xl font-bold text-white line-clamp-1">
|
||||
{fixPackageName(pkg.name)}
|
||||
</h3>
|
||||
{#if pkg.state === PackageStates.INSTALLED}
|
||||
<i class="icon-check-circle-o mb-1 ml-2 flex text-3xl text-[#00ffd0]" />
|
||||
{/if}
|
||||
</div>
|
||||
<p class="line-clamp-[8] h-[160px] text-[14px] font-thin lowercase leading-[20px]">
|
||||
{pkg.desc ?? ""}
|
||||
</p>
|
||||
|
@ -125,6 +139,15 @@
|
|||
box-shadow: 0px 0px 0px 2px rgba(128, 0, 255, 0.5);
|
||||
}
|
||||
|
||||
section.package-card.updated {
|
||||
border-color: #00ffd0;
|
||||
}
|
||||
|
||||
section.package-card.updated:active {
|
||||
border-color: #00ffd0;
|
||||
box-shadow: 0px 0px 0px 2px rgba(0, 255, 208, 0.5);
|
||||
}
|
||||
|
||||
.content-container {
|
||||
height: 50%;
|
||||
background: linear-gradient(180deg, rgba(26, 26, 26, 0.3) 0%, rgba(26, 26, 26, 0.75) 72.92%);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { PackageStates, type GUIPackage } from "$libs/types";
|
||||
import Button from "@tea/ui/button/button.svelte";
|
||||
import { t } from "$libs/translations";
|
||||
import { getPackageBadgeText } from "$libs/packages/pkg-utils";
|
||||
|
||||
export let buttonSize: "small" | "large" = "small";
|
||||
|
||||
|
@ -44,7 +44,7 @@
|
|||
|
||||
const hasVersionSelectorDropdown = !!$$slots.selector;
|
||||
|
||||
$: ctaLabel = $t(`package.cta-${pkg.state}`);
|
||||
$: ctaLabel = getPackageBadgeText(pkg);
|
||||
</script>
|
||||
|
||||
<Button
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { getPackageBadgeText } from "$libs/packages/pkg-utils";
|
||||
import type { GUIPackage } from "$libs/types";
|
||||
|
||||
export let pkg: GUIPackage | null = null;
|
||||
export let pkg: GUIPackage;
|
||||
</script>
|
||||
|
||||
<div class="container relative h-full" data-testid={`install-badge-${pkg?.slug}`}>
|
||||
<div class="content flex items-center justify-center gap-2 p-2">
|
||||
<i class="icon-check-circle-o flex text-sm text-[#00ffd0]" />
|
||||
<div class="text-xs">INSTALLED</div>
|
||||
<div class="text-xs">{getPackageBadgeText(pkg)}</div>
|
||||
<div class="rounded-sm bg-white px-1 text-[10px] leading-[12px] text-black">
|
||||
v{pkg?.installed_versions?.[0]}
|
||||
</div>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
import NoUpdates from "./no-updates.svelte";
|
||||
import { packagesStore, scrollStore } from "$libs/stores";
|
||||
import { afterUpdate, beforeUpdate } from "svelte";
|
||||
import { packageWasUpdated } from "$libs/packages/pkg-utils";
|
||||
|
||||
const { packageList: allPackages } = packagesStore;
|
||||
export let packageFilter: SideMenuOptions = SideMenuOptions.all;
|
||||
|
@ -38,7 +39,10 @@
|
|||
].includes(pkg.state);
|
||||
},
|
||||
[SideMenuOptions.installed_updates_available]: (pkg: GUIPackage) => {
|
||||
return [PackageStates.UPDATING, PackageStates.NEEDS_UPDATE].includes(pkg.state);
|
||||
return (
|
||||
[PackageStates.UPDATING, PackageStates.NEEDS_UPDATE].includes(pkg.state) ||
|
||||
packageWasUpdated(pkg)
|
||||
);
|
||||
},
|
||||
[SideMenuOptions.recently_updated]: (pkg: GUIPackage) => {
|
||||
return moment(pkg.last_modified).isAfter(moment().subtract(30, "days"));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import log from "$libs/logger";
|
||||
import type { GUIPackage } from "$libs/types";
|
||||
import SemVer from "@teaxyz/lib/semver";
|
||||
import { t } from "$libs/translations";
|
||||
|
||||
// Find a list of available versions for a package based on the bottles
|
||||
export const findAvailableVersions = (pkg: Pick<GUIPackage, "bottles" | "version">) => {
|
||||
|
@ -67,3 +68,21 @@ export const isPackageUpToDate = (pkg: GUIPackage) => {
|
|||
// if the installed version is equal or newer than the latest version, it's up to date
|
||||
return semverCompare(pkg.installed_versions[0], pkg.version) >= 0;
|
||||
};
|
||||
|
||||
export const packageWasInstalled = (pkg: GUIPackage) => {
|
||||
return pkg.displayState?.state === "INSTALLED";
|
||||
};
|
||||
|
||||
export const packageWasUpdated = (pkg: GUIPackage) => {
|
||||
return pkg.displayState?.state === "UPDATED";
|
||||
};
|
||||
|
||||
export const packageHadError = (pkg: GUIPackage) => {
|
||||
return pkg.displayState?.state === "ERROR";
|
||||
};
|
||||
|
||||
export const getPackageBadgeText = (pkg: GUIPackage) => {
|
||||
// UPDATED is a "pseudo-state" that overrides other states
|
||||
const state = packageWasUpdated(pkg) ? "UPDATED" : pkg.state;
|
||||
return t.get(`package.cta-${state}`);
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
import { getReadme, getContributors, getRepoAsPackage } from "$libs/github";
|
||||
import { NotificationType } from "@tea/ui/types";
|
||||
import { trackInstall, trackInstallFailed } from "$libs/analytics";
|
||||
import { addInstalledVersion, isInstalling } from "$libs/packages/pkg-utils";
|
||||
import { addInstalledVersion, isInstalling, packageWasUpdated } from "$libs/packages/pkg-utils";
|
||||
import withDebounce from "$libs/utils/debounce";
|
||||
import { trimGithubSlug } from "$libs/github";
|
||||
import { notificationStore } from "$libs/stores";
|
||||
|
@ -230,19 +230,18 @@ const installPkg = async (pkg: GUIPackage, version?: string) => {
|
|||
await installPackage(pkg, versionToInstall);
|
||||
trackInstall(pkg.full_name);
|
||||
|
||||
// If the package was AVAILABLE previously then it was just installed, otherwise it was updated
|
||||
const state = pkg.state === PackageStates.AVAILABLE ? "INSTALLED" : "UPDATED";
|
||||
updatePackage(pkg.full_name, { displayState: { state, version: versionToInstall } });
|
||||
|
||||
await refreshSinglePackage(pkg.full_name);
|
||||
if (pkg.state === PackageStates.AVAILABLE) {
|
||||
updatePackage(pkg.full_name, {
|
||||
displayState: { kind: "INSTALLED_SUCCESSFULLY", version: versionToInstall }
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
let message = "Unknown Error";
|
||||
if (error instanceof Error) message = error.message;
|
||||
trackInstallFailed(pkg.full_name, message || "unknown");
|
||||
updatePackage(pkg.full_name, {
|
||||
displayState: { kind: "INSTALLATION_ERROR", errorMessage: message, version: versionToInstall }
|
||||
displayState: { state: "ERROR", errorMessage: message, version: versionToInstall }
|
||||
});
|
||||
} finally {
|
||||
updatePackage(pkg.full_name, { install_progress_percentage: 100 });
|
||||
|
@ -364,6 +363,19 @@ const resetPackageDisplayState = (pkg: GUIPackage) => {
|
|||
});
|
||||
};
|
||||
|
||||
const resetAllPackagesUpdatedState = () => {
|
||||
packageMap.update((pkgs) => {
|
||||
Object.values(pkgs.packages).forEach((pkg) => {
|
||||
// only reset the display state if the package was updated,
|
||||
// installed and error display states should not be reset here
|
||||
if (packageWasUpdated(pkg)) {
|
||||
pkg.displayState = null;
|
||||
}
|
||||
});
|
||||
return pkgs;
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
packageList,
|
||||
search: searchPackages,
|
||||
|
@ -374,5 +386,6 @@ export default {
|
|||
deletePkg,
|
||||
destroy,
|
||||
cachePkgImage,
|
||||
resetPackageDisplayState
|
||||
resetPackageDisplayState,
|
||||
resetAllPackagesUpdatedState
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"cta-UNINSTALL": "UNINSTALL",
|
||||
"cta-NEEDS_UPDATE": "UPDATE",
|
||||
"cta-UPDATING": "UPDATING",
|
||||
"cta-UPDATED": "UPDATED",
|
||||
"cta-PRUNE": "PRUNE",
|
||||
"cta-PRUNING": "PRUNING"
|
||||
},
|
||||
|
|
|
@ -13,12 +13,13 @@ export enum PackageStates {
|
|||
UPDATING = "UPDATING"
|
||||
}
|
||||
|
||||
// PackageDisplayState is for showing temporary UI elements when a package is installed or updated. It is not persisted.
|
||||
// PackageDisplayState is a TEMPORARY state for showing temporary UI elements when a package is
|
||||
// installed or updated. It is not persisted.
|
||||
export interface PackageDisplayState {
|
||||
// INSTALLED_SUCCESSFULLY -> The pacakge was installed successfully show some confetti!
|
||||
// UPDATED_SUCCESSFULLY -> The package was updated successfully change the badge temporarily
|
||||
// INSTALLATION_ERROR -> The package failed to install show an error overlay temporarily
|
||||
kind: "INSTALLED_SUCCESSFULLY" | "UPDATED_SUCCESSFULLY" | "INSTALLATION_ERROR";
|
||||
// INSTALLED -> The package was installed successfully. Let's show some confetti!
|
||||
// UPDATED -> The package was updated successfully so change the badge temporarily
|
||||
// ERROR -> The package failed to install so show an error overlay temporarily
|
||||
state: "INSTALLED" | "UPDATED" | "ERROR";
|
||||
errorMessage?: string;
|
||||
version: string;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
const fromPath = from?.url.href.replace(from.url.origin, "");
|
||||
setNewPath(nextPath, fromPath || "/");
|
||||
}
|
||||
|
||||
// the updated state should be reset any time the user navigates
|
||||
packagesStore.resetAllPackagesUpdatedState();
|
||||
});
|
||||
|
||||
const syncPath = async () => {
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
}
|
||||
}
|
||||
updating = false;
|
||||
sideMenuOption = SideMenuOptions.all;
|
||||
}
|
||||
|
||||
$: needsUpdateCount = pkgsToUpdate.length;
|
||||
|
|
|
@ -83,7 +83,6 @@ describe("basic smoke test", () => {
|
|||
await expect(updateBtn).toExist();
|
||||
updateBtn.click();
|
||||
|
||||
// FIXME: This should test for the button saying "UPDATED", but that feature is not done yet so uncomment this when it is
|
||||
//await utils.verifyInstalledBadge(slug, "UPDATED");
|
||||
await utils.verifyInstalledBadge(slug, "UPDATED");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -91,7 +91,7 @@ export function setupUtils(browser: WebdriverIO.Browser) {
|
|||
|
||||
const verifyInstalledBadge = async (
|
||||
slug: string,
|
||||
state: "INSTALLED" | "UPDATE" = "INSTALLED"
|
||||
state: "INSTALLED" | "UPDATE" | "UPDATED" = "INSTALLED"
|
||||
) => {
|
||||
// wait 30 seconds for the badge to show up
|
||||
for (let i = 0; i < 30; i++) {
|
||||
|
|
Loading…
Reference in a new issue