mirror of
https://github.com/ivabus/gui
synced 2025-04-23 05:57:11 +03:00
New image format (#675)
* use new image loading strategy: infer from gui.tea.xyz/stage/pkgFullname/dimension.webp
This commit is contained in:
parent
0dc057af8d
commit
53224e21b9
10 changed files with 88 additions and 106 deletions
|
@ -3,10 +3,12 @@ import { mkdirp } from "mkdirp";
|
|||
import fs from "fs";
|
||||
import log from "./logger";
|
||||
import { getTeaPath } from "./tea-dir";
|
||||
import { Packages } from "../../src/libs/types";
|
||||
import { GUIPackage, Packages } from "../../src/libs/types";
|
||||
import { isDev } from "./auto-updater";
|
||||
|
||||
const pkgsFilePath = path.join(getTeaPath(), "tea.xyz/gui/pkgs.json");
|
||||
const pkgsFolder = path.join(getTeaPath(), "tea.xyz/gui");
|
||||
const dev = isDev();
|
||||
|
||||
export async function writePackageCache(pkgs: Packages) {
|
||||
try {
|
||||
|
@ -29,13 +31,19 @@ export async function loadPackageCache(): Promise<Packages> {
|
|||
log.info(`loading package cache from ${pkgsFilePath}`);
|
||||
const pkgData = fs.readFileSync(pkgsFilePath);
|
||||
const pkgs = JSON.parse(pkgData.toString()) as Packages;
|
||||
|
||||
if (pkgs?.packages) {
|
||||
// Remove any temporary properties that may have been added to the package (like installation progress)
|
||||
for (const [key, value] of Object.entries(pkgs.packages)) {
|
||||
const { install_progress_percentage, isUninstalling, synced, displayState, ...rest } =
|
||||
value;
|
||||
pkgs.packages[key] = rest;
|
||||
pkgs.packages[key] = rest as GUIPackage;
|
||||
// possible user deletes cache files
|
||||
delete pkgs.packages[key].cached_image_url;
|
||||
if (rest.image_added_at) {
|
||||
const prefix = `https://gui.tea.xyz/${dev ? "dev" : "prod"}/${rest.full_name}`;
|
||||
pkgs.packages[key].image_128_url = `${prefix}/128x128.webp`;
|
||||
pkgs.packages[key].image_512_url = `${prefix}/512x512.webp`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pkgs;
|
||||
|
|
|
@ -163,18 +163,24 @@ async function downloadImage(url: string, imagePath: string): Promise<void> {
|
|||
const response = await fetch(url);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const fileStream = fs.createWriteStream(imagePath);
|
||||
response.body.pipe(fileStream);
|
||||
fileStream.on("finish", () => resolve());
|
||||
fileStream.on("error", (error) => reject(error));
|
||||
if (response.status == 200) {
|
||||
response.body.pipe(fileStream);
|
||||
fileStream.on("finish", () => resolve());
|
||||
fileStream.on("error", (error) => reject(error));
|
||||
} else {
|
||||
reject(new Error(`Failed to download image: ${url}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function cacheImage(url: string): Promise<string> {
|
||||
const imageFolder = path.join(getGuiPath(), "cached_images");
|
||||
const imageName = path.basename(url);
|
||||
const imagePath = path.join(imageFolder, imageName);
|
||||
|
||||
await mkdirp(imageFolder);
|
||||
const pkgFilePath = url.split("gui.tea.xyz")[1];
|
||||
const imagePath = path.join(imageFolder, pkgFilePath);
|
||||
const fileName = path.basename(imagePath);
|
||||
const folderPath = imagePath.replace(fileName, "");
|
||||
log.info("creating folder:", folderPath);
|
||||
await mkdirp(folderPath);
|
||||
|
||||
if (!fs.existsSync(imagePath)) {
|
||||
try {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tea",
|
||||
"version": "0.2.27",
|
||||
"version": "0.2.28",
|
||||
"private": true,
|
||||
"description": "tea gui app",
|
||||
"author": "tea.xyz",
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
<script lang="ts">
|
||||
import "$appcss";
|
||||
import { onMount } from "svelte";
|
||||
import type { Package } from "@tea/ui/types";
|
||||
|
||||
import Gallery from "@tea/ui/gallery/gallery.svelte";
|
||||
import {
|
||||
featuredPackages as featuredPackagesStore,
|
||||
initializeFeaturedPackages
|
||||
} from "$libs/stores";
|
||||
|
||||
let featuredPackages: Package[] = [];
|
||||
|
||||
featuredPackagesStore.subscribe((v) => {
|
||||
featuredPackages = v;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
if (!featuredPackages.length) {
|
||||
initializeFeaturedPackages();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Gallery
|
||||
title="FEATURED PACKAGES"
|
||||
items={featuredPackages.map((pkg) => ({
|
||||
title: pkg.full_name,
|
||||
subTitle: pkg.maintainer || "",
|
||||
imageUrl: pkg.thumb_image_url,
|
||||
link: `/packages/${pkg.slug}`
|
||||
}))}
|
||||
/>
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { GUIPackage } from "$libs/types";
|
||||
import { onMount } from "svelte";
|
||||
import { packagesStore } from "$libs/stores";
|
||||
|
||||
let clazz = "";
|
||||
|
@ -15,8 +14,8 @@
|
|||
let loaded = false;
|
||||
let lastProcessedPkg: GUIPackage | null = null;
|
||||
|
||||
const loadImage = async (url: string): Promise<string> => {
|
||||
if (url.includes("cached_images")) {
|
||||
const loadImage = async (url: string, force = false): Promise<string> => {
|
||||
if (url.includes("cached_images") || force) {
|
||||
loadedImg = url;
|
||||
loaded = true;
|
||||
return url;
|
||||
|
@ -38,21 +37,12 @@
|
|||
});
|
||||
};
|
||||
|
||||
const recachePkg = async () => {
|
||||
const url = await packagesStore.cachePkgImage(pkg);
|
||||
loadImage(url);
|
||||
};
|
||||
|
||||
const getCache = async () => {
|
||||
if (pkg.cached_image_url) {
|
||||
loadImage(pkg.cached_image_url).catch(() => {
|
||||
if (pkg.thumb_image_url) {
|
||||
loadImage(pkg.thumb_image_url);
|
||||
recachePkg();
|
||||
}
|
||||
if (pkg.image_512_url) {
|
||||
loadImage(pkg.image_512_url, true).finally(async () => {
|
||||
const nextImage = await packagesStore.cachePkgImage(pkg);
|
||||
loadImage(nextImage, true);
|
||||
});
|
||||
} else if (pkg.thumb_image_url) {
|
||||
recachePkg();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -69,7 +59,7 @@
|
|||
<section class="overflow-hidden bg-black {clazz} {layout}">
|
||||
<i
|
||||
class="logo icon-tea-logo-iconasset-1 text-3xl text-gray {layout}"
|
||||
class:animate-pulse={!pkg.thumb_image_url}
|
||||
class:animate-pulse={!pkg.image_added_at}
|
||||
/>
|
||||
<div
|
||||
class="opacity-0 transition-all duration-500 {layout}"
|
||||
|
|
|
@ -24,9 +24,7 @@
|
|||
<ImgLoader
|
||||
on:click={() => gotoPackagePage()}
|
||||
class="pkg-image h-16 w-16 object-cover"
|
||||
src={!pkg.thumb_image_url.includes("https://tea.xyz")
|
||||
? "/images/default-thumb.jpg"
|
||||
: pkg.thumb_image_url}
|
||||
src={pkg.image_128_url || "/images/default-thumb.jpg"}
|
||||
alt={pkg.name}
|
||||
/>
|
||||
<header data-testid="card-result-{pkg.slug}" class="flex-grow" on:click={() => gotoPackagePage()}>
|
||||
|
|
|
@ -20,15 +20,14 @@ const packages: Package[] = [
|
|||
last_modified: "2022-10-06T15:45:08.000Z",
|
||||
full_name: "mesonbuild.com",
|
||||
dl_count: 270745,
|
||||
thumb_image_name: "mesonbuild_com_option 1.jpg ",
|
||||
maintainer: "",
|
||||
desc: "Fast and user friendly build system",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/mesonbuild_com.jpg",
|
||||
installs: 0,
|
||||
categories: ["foundation_essentials"],
|
||||
created: "2022-10-06T15:45:08.000Z",
|
||||
manual_sorting: 0,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "pixman_org",
|
||||
|
@ -39,14 +38,13 @@ const packages: Package[] = [
|
|||
last_modified: "2022-09-26T19:37:47.000Z",
|
||||
full_name: "pixman.org",
|
||||
dl_count: 0,
|
||||
thumb_image_name: "pixman_org_option 1.jpg ",
|
||||
desc: "Pixman is a library that provides low-level pixel manipulation features such as image compositing and trapezoid rasterization.",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/pixman_org.jpg",
|
||||
installs: 0,
|
||||
categories: ["foundation_essentials"],
|
||||
created: "2022-09-26T19:37:47.000Z",
|
||||
manual_sorting: 1,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "freedesktop_org_pkg_config",
|
||||
|
@ -57,14 +55,13 @@ const packages: Package[] = [
|
|||
last_modified: "2022-10-20T01:32:15.000Z",
|
||||
full_name: "freedesktop.org/pkg-config",
|
||||
dl_count: 2661501,
|
||||
thumb_image_name: "freedecktop_org_pkg_config option 1.jpg ",
|
||||
desc: "Manage compile and link flags for libraries",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/freedesktop_org_pkg_config.jpg",
|
||||
installs: 0,
|
||||
categories: ["foundation_essentials"],
|
||||
created: "2022-10-20T01:32:15.000Z",
|
||||
manual_sorting: 2,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "gnu_org_gettext",
|
||||
|
@ -75,14 +72,13 @@ const packages: Package[] = [
|
|||
last_modified: "2022-10-20T01:23:46.000Z",
|
||||
full_name: "gnu.org/gettext",
|
||||
dl_count: 3715970,
|
||||
thumb_image_name: "gnu_org_gettext_option 1.jpg ",
|
||||
desc: "GNU internationalization (i18n) and localization (l10n) library",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/gnu_org_gettext.jpg",
|
||||
installs: 0,
|
||||
categories: ["foundation_essentials"],
|
||||
created: "2022-10-20T01:23:46.000Z",
|
||||
manual_sorting: 3,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "ipfs_tech",
|
||||
|
@ -92,15 +88,14 @@ const packages: Package[] = [
|
|||
last_modified: "2022-10-19T21:36:52.000Z",
|
||||
full_name: "ipfs.tech",
|
||||
dl_count: 14457,
|
||||
thumb_image_name: "ipfs_tech_option 2.jpg ",
|
||||
maintainer: "",
|
||||
desc: "Peer-to-peer hypermedia protocol",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/ipfs_tech.jpg",
|
||||
installs: 0,
|
||||
categories: ["foundation_essentials"],
|
||||
created: "2022-10-19T21:36:52.000Z",
|
||||
manual_sorting: 4,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "nixos_org_patchelf",
|
||||
|
@ -111,14 +106,13 @@ const packages: Package[] = [
|
|||
last_modified: "2022-09-27T04:50:44.000Z",
|
||||
full_name: "nixos.org/patchelf",
|
||||
dl_count: 0,
|
||||
thumb_image_name: "nixos_org_patchelf_option 1.jpg ",
|
||||
desc: "PatchELF is a simple utility for modifying existing ELF executables and libraries.",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/nixos_org_patchelf.jpg",
|
||||
installs: 0,
|
||||
categories: ["top_packages", "foundation_essentials"],
|
||||
created: "2022-09-27T04:50:44.000Z",
|
||||
manual_sorting: 5,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "tea_xyz",
|
||||
|
@ -129,14 +123,13 @@ const packages: Package[] = [
|
|||
last_modified: "2022-10-19T19:13:51.000Z",
|
||||
full_name: "tea.xyz",
|
||||
dl_count: 0,
|
||||
thumb_image_name: "tea_xyz_option 2.jpg ",
|
||||
desc: "Website of tea.xyz",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/tea_xyz.jpg",
|
||||
installs: 0,
|
||||
categories: ["top_packages", "foundation_essentials"],
|
||||
created: "2022-10-19T19:13:51.000Z",
|
||||
manual_sorting: 6,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "charm_sh_gum",
|
||||
|
@ -147,14 +140,13 @@ const packages: Package[] = [
|
|||
last_modified: "2022-10-21T02:15:16.000Z",
|
||||
full_name: "charm.sh/gum",
|
||||
dl_count: 0,
|
||||
thumb_image_name: "charm_sh_gum.jpg ",
|
||||
desc: "",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/charm_sh_gum.jpg",
|
||||
installs: 0,
|
||||
categories: ["top_packages", "foundation_essentials"],
|
||||
created: "2022-10-21T02:15:16.000Z",
|
||||
manual_sorting: 7,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "pyyaml_org",
|
||||
|
@ -164,15 +156,14 @@ const packages: Package[] = [
|
|||
last_modified: "2022-10-03T15:35:14.000Z",
|
||||
full_name: "pyyaml.org",
|
||||
dl_count: 107505,
|
||||
thumb_image_name: "pyyaml_org_option 1.jpg ",
|
||||
maintainer: "",
|
||||
desc: "YAML framework for Python",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/pyyaml_org.jpg",
|
||||
installs: 0,
|
||||
categories: ["top_packages", "foundation_essentials"],
|
||||
created: "2022-10-03T15:35:14.000Z",
|
||||
manual_sorting: 8,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
},
|
||||
{
|
||||
slug: "tea_xyz_gx_cc",
|
||||
|
@ -183,14 +174,13 @@ const packages: Package[] = [
|
|||
last_modified: "2022-10-19T16:47:44.000Z",
|
||||
full_name: "tea.xyz/gx/cc",
|
||||
dl_count: 0,
|
||||
thumb_image_name: "tea_xyz_gx.jpg ",
|
||||
desc: "",
|
||||
thumb_image_url: "https://tea.xyz/Images/packages/tea_xyz_gx_cc.jpg",
|
||||
installs: 0,
|
||||
categories: ["top_packages", "foundation_essentials"],
|
||||
created: "2022-10-19T16:47:44.000Z",
|
||||
manual_sorting: 9,
|
||||
card_layout: "bottom"
|
||||
card_layout: "bottom",
|
||||
image_added_at: new Date()
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ import {
|
|||
listenToChannel,
|
||||
getInstalledVersionsForPackage,
|
||||
monitorTeaDir,
|
||||
stopMonitoringTeaDir
|
||||
stopMonitoringTeaDir,
|
||||
isDev
|
||||
} from "@native";
|
||||
|
||||
import { getReadme, getContributors, getRepoAsPackage } from "$libs/github";
|
||||
|
@ -38,6 +39,8 @@ let initialized = false;
|
|||
let isDestroyed = false;
|
||||
let refreshTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
const dev = isDev();
|
||||
|
||||
const packageMap = writable<Packages>({ version: "0", packages: {} });
|
||||
const packageList = derived(packageMap, ($packages) =>
|
||||
Object.values($packages.packages).sort((a, b) => {
|
||||
|
@ -162,12 +165,22 @@ const refreshPackages = async () => {
|
|||
if (isDestroyed) return;
|
||||
|
||||
log.info("packages store: refreshing...");
|
||||
|
||||
const isDev = await dev;
|
||||
const pkgs = await getDistPackages();
|
||||
const guiPkgs: GUIPackage[] = pkgs.map((p) => ({
|
||||
...p,
|
||||
state: PackageStates.AVAILABLE
|
||||
}));
|
||||
|
||||
const guiPkgs: GUIPackage[] = pkgs.map((p) => {
|
||||
const prefix = `https://gui.tea.xyz/${isDev ? "dev" : "prod"}/${p.full_name}`;
|
||||
return {
|
||||
...p,
|
||||
state: PackageStates.AVAILABLE,
|
||||
...(p.image_added_at
|
||||
? {
|
||||
image_512_url: `${prefix}/512x512.webp`,
|
||||
image_128_url: `${prefix}/128x128.webp`
|
||||
}
|
||||
: {})
|
||||
};
|
||||
});
|
||||
|
||||
if (!initialized) {
|
||||
// set packages data so that i can render something in the UI already
|
||||
|
@ -291,8 +304,8 @@ packageMap.subscribe(async (pkgs) => {
|
|||
const cachePkgImage = async (pkg: GUIPackage): Promise<string> => {
|
||||
let cacheFileURL = "";
|
||||
updatePackage(pkg.full_name, { cached_image_url: "" });
|
||||
if (pkg.thumb_image_url && !pkg.thumb_image_url.includes("package-thumb-nolabel4.jpg")) {
|
||||
const result = await cacheImageURL(pkg.thumb_image_url);
|
||||
if (pkg.image_added_at && pkg.image_512_url) {
|
||||
const result = await cacheImageURL(pkg.image_512_url);
|
||||
if (result) {
|
||||
cacheFileURL = result;
|
||||
updatePackage(pkg.full_name, { cached_image_url: cacheFileURL });
|
||||
|
@ -301,6 +314,15 @@ const cachePkgImage = async (pkg: GUIPackage): Promise<string> => {
|
|||
return cacheFileURL;
|
||||
};
|
||||
|
||||
export const getPackageImageURL = async (
|
||||
pkg: GUIPackage,
|
||||
size: 512 | 1024 | 128
|
||||
): Promise<string> => {
|
||||
if (!pkg.image_added_at) return "";
|
||||
const isDev = await dev;
|
||||
return `https://gui.tea.xyz/${isDev ? "dev" : "prod"}/${pkg.full_name}/${size}x${size}.webp`;
|
||||
};
|
||||
|
||||
listenToChannel("install-progress", ({ full_name, progress }: any) => {
|
||||
if (!full_name) {
|
||||
return;
|
||||
|
@ -387,5 +409,6 @@ export default {
|
|||
destroy,
|
||||
cachePkgImage,
|
||||
resetPackageDisplayState,
|
||||
resetAllPackagesUpdatedState
|
||||
resetAllPackagesUpdatedState,
|
||||
getPackageImageURL
|
||||
};
|
||||
|
|
|
@ -36,7 +36,8 @@ export type GUIPackage = Package & {
|
|||
install_progress_percentage?: number;
|
||||
isUninstalling?: boolean;
|
||||
cached_image_url?: string;
|
||||
|
||||
image_512_url?: string;
|
||||
image_128_url?: string;
|
||||
displayState?: PackageDisplayState | null;
|
||||
};
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ export interface Package {
|
|||
homepage: string;
|
||||
last_modified: Date | string;
|
||||
created: Date | string;
|
||||
thumb_image_url: string;
|
||||
thumb_image_name: string;
|
||||
desc: string;
|
||||
dl_count: number;
|
||||
installs: number;
|
||||
|
@ -37,6 +35,7 @@ export interface Package {
|
|||
};
|
||||
manual_sorting: number;
|
||||
card_layout: "bottom" | "right" | "left";
|
||||
image_added_at: Date | null;
|
||||
}
|
||||
|
||||
export type AirtablePost = {
|
||||
|
|
Loading…
Reference in a new issue