version select when installing (#349)

- drop down resolve #349
This commit is contained in:
ABevier 2023-03-28 23:09:50 -04:00 committed by GitHub
parent 8766702c35
commit d6dbffa6e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 184 additions and 35 deletions

View file

@ -6,11 +6,13 @@
import type { GUIPackage } from '$libs/types';
import { PackageStates } from '$libs/types';
import { getPackage } from '@native';
import { notificationStore } from '$libs/stores';
import { packagesStore, notificationStore } from '$libs/stores';
import { onMount } from 'svelte';
import semverCompare from "semver/functions/compare";
export let tab = "all";
export let pkg: GUIPackage;
export let onClick: () => void;
export let onClick: (version: string) => void;
let fakeLoadingProgress = 0;
@ -41,31 +43,50 @@
fakeLoadingProgress = fakeLoadingProgress + addProgress;
}, ms);
}
const onClickCTA = async (version: string) => {
fakeLoadingProgress = 1;
startFakeLoader();
await onClick(version);
await new Promise((resolve) => {
setTimeout(() => {
notificationStore.add({
message: `Package ${pkg.full_name} v${pkg.version} has been installed.`,
});
clearTimeout(fakeTimer);
fakeLoadingProgress = 100;
resolve(null);
}, 1500);
});
}
const findAvailableVersions = (pkg: GUIPackage) => {
// default to just showing the latest if bottles haven't loaded yet
if (!pkg.bottles) {
return [pkg.version]
}
const versionSet = new Set<string>()
for (const b of pkg.bottles) {
versionSet.add(b.version)
}
return Array.from(versionSet).sort((a, b) => semverCompare(b, a));
}
onMount(() => {
packagesStore.fetchPackageBottles(pkg.full_name)
});
</script>
<PackageCard
{pkg}
availableVersions={findAvailableVersions(pkg)}
link={`/packages/${pkg.slug}?tab=${tab}`}
ctaLabel={getCTALabel(pkg.state)}
progessLoading={+fakeLoadingProgress.toFixed(2)}
ctaType="plain"
ctaColor={PackageStates.INSTALLED === pkg.state ? "green" : "secondary"}
onClickCTA={((pkg) => {
return async () => {
fakeLoadingProgress = 1;
startFakeLoader();
await onClick();
await new Promise((resolve) => {
setTimeout(() => {
notificationStore.add({
message: `Package ${pkg.full_name} v${pkg.version} has been installed.`,
});
clearTimeout(fakeTimer);
fakeLoadingProgress = 100;
resolve(null);
}, 1500);
});
}
})(pkg)}
/>
{onClickCTA}
/>

View file

@ -60,7 +60,7 @@
<Package
tab={packageFilter}
{pkg}
onClick={() => packagesStore.installPkg(pkg)}
onClick={(version) => packagesStore.installPkg(pkg, version)}
/>
</div>
{/if}
@ -94,4 +94,4 @@
box-shadow: inset 0vw 0vw 0vw 0.223vw #1a1a1a !important;
box-sizing: border-box;
}
</style>
</style>

View file

@ -91,6 +91,7 @@ export async function installPackage(pkg: GUIPackage, version?: string) {
try {
const latestVersion = pkg.version;
const specificVersion = version || latestVersion;
log.info(`installing package: ${pkg.name} version: ${specificVersion}`);
await installPackageCommand(pkg.full_name + (specificVersion ? `@${specificVersion}` : ""));
} catch (error) {
log.error("installPackage:", error);

View file

@ -1,7 +1,6 @@
import { writable } from "svelte/store";
import { nanoid } from "nanoid";
import { l } from "$libs/translations";
import { NotificationType } from "@tea/ui/types";
import type { Notification } from "@tea/ui/types";

View file

@ -7,7 +7,8 @@ import {
getDistPackages,
openTerminal,
getInstalledPackages,
installPackage
installPackage,
getPackageBottles
} from "@native";
import { getReadme, getContributors, getRepoAsPackage } from "$libs/github";
@ -157,6 +158,12 @@ To read more about this package go to [${guiPkg.homepage}](${guiPkg.homepage}).
}
};
const fetchPackageBottles = async (pkgName: string) => {
// TODO: this api should take an architecture argument or else an architecture filter should be applied downstreawm
const bottles = await getPackageBottles(pkgName);
updatePackage(pkgName, { bottles });
};
return {
packages,
syncProgress,
@ -178,6 +185,7 @@ To read more about this package go to [${guiPkg.homepage}](${guiPkg.homepage}).
}
});
},
fetchPackageBottles,
updatePackage,
init,
installPkg

View file

@ -3,7 +3,7 @@
// please use the package @tea/ui/src/types.ts
// things that go there are shared types/shapes like ie: Package
import type { Package, Developer, Bottle } from "@tea/ui/types";
import type { Package, Developer } from "@tea/ui/types";
export enum PackageStates {
AVAILABLE = "AVAILABLE",

View file

@ -29,5 +29,13 @@ module.exports = {
browser: true,
es2017: true,
node: true
},
rules: {
"@typescript-eslint/no-unused-vars": [
"warn",
{
argsIgnorePattern: "^_"
}
]
}
};

View file

@ -9,3 +9,11 @@ declare namespace App {
// interface Error {}
// interface Platform {}
}
// Declare custom event handlers here to make typscript happy.
declare namespace svelte.JSX {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface HTMLAttributes<T> {
onclick_outside: () => void;
}
}

View file

@ -0,0 +1,20 @@
/**
* Use this script on an element with use:clickOutside to register for click events that occur
* outside of the element
*/
export default function clickOutside(element: HTMLElement) {
const handleClick = (e: MouseEvent) => {
const event = e as MouseEvent & { target?: Node };
if (element && !element.contains(event.target) && !event.defaultPrevented) {
element.dispatchEvent(new CustomEvent("click_outside"));
}
};
document.addEventListener("click", handleClick, true);
return {
destroy() {
document.removeEventListener("click", handleClick, true);
}
};
}

View file

@ -0,0 +1,89 @@
<script lang="ts">
import clickOutside from "../lib/clickOutside";
import Button from "../button/button.svelte";
export let ctaLabel: string;
export let ctaType: "ghost" | "plain" = "ghost";
export let ctaColor: "green" | "secondary" = "secondary";
export let onClickCTA = async (_version: string) => {
console.log("do nothing");
};
export let availableVersions: string[] = [];
$: isOpened = false;
const handleClick = (version: string) => {
isOpened = false;
onClickCTA(version);
};
const handleClickOutside = () => (isOpened = false);
</script>
<div class="dropdown" use:clickOutside on:click_outside={handleClickOutside}>
<Button
class="h-8 border border-gray p-2 text-xs"
type={ctaType}
color={ctaColor}
onClick={() => (isOpened = !isOpened)}
>
{ctaLabel}
</Button>
<div class="version-list" class:visible={isOpened}>
{#each availableVersions as version, idx}
{#if idx !== 0}<hr class="divider" />{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="version-item text-xs" on:click={() => handleClick(version)}>
v{version + (idx === 0 ? " (latest)" : "")}
</div>
{/each}
</div>
</div>
<style>
.version-list {
display: none;
position: absolute;
margin-top: 4px;
width: 100%;
z-index: 1;
background-color: #1a1a1a;
border: 0.5px solid #949494;
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.5);
border-radius: 2px;
max-height: 160px;
overflow-y: auto;
}
.version-item {
cursor: pointer;
margin: 4px 6px;
padding: 4px 6px;
height: auto;
width: auto;
white-space: nowrap;
}
.version-item:hover {
border: 1px solid #949494;
background-color: rgba(148, 148, 148, 0.35);
}
.dropdown {
position: relative;
display: inline-block;
width: 130px;
}
.divider {
border: 1px solid #272626;
margin-left: 8px;
margin-right: 8px;
}
.visible {
display: block;
}
</style>

View file

@ -2,17 +2,18 @@
import "../app.css";
import type { Package } from "../types";
import ImgLoader from "../img-loader/img-loader.svelte";
import Button from "../button/button.svelte";
import ProgressCircle from "../progress-circle/progress-circle.svelte";
import InstallButton from "./install-button.svelte";
export let pkg: Package;
export let availableVersions: string[];
export let link: string;
export let ctaLabel: string;
export let progessLoading = 0;
export let ctaType: "ghost" | "plain" = "ghost";
export let ctaColor: "green" | "secondary" = "secondary";
export let onClickCTA = () => {
export let onClickCTA = async (_version: string) => {
console.log("do nothing");
};
</script>
@ -48,14 +49,7 @@
<span class="package-install-no">>{{- .installs -}}&nbsp;installs</span> -->
</p>
</div>
<Button
class="h-8 w-1/2 border border-gray p-2 text-xs"
onClick={onClickCTA}
type={ctaType}
color={ctaColor}
>
{ctaLabel}
</Button>
<InstallButton {ctaLabel} {ctaColor} {ctaType} {onClickCTA} {availableVersions} />
</footer>
{#if progessLoading > 0 && progessLoading < 100}
<div class="absolute top-0 left-0 h-full w-full bg-black bg-opacity-50">

View file

@ -21,6 +21,7 @@
},
"include": [
"ambient.d.ts",
"./src/app.d.ts",
"./types/**/$types.d.ts",
"../vite.config.ts",
"../src/**/*.js",