Skip to content

Commit

Permalink
feat: implement upgrade feature in store
Browse files Browse the repository at this point in the history
  • Loading branch information
HuakunShen committed Sep 13, 2024
1 parent 5228b66 commit 57a65f2
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 19 deletions.
24 changes: 21 additions & 3 deletions apps/desktop/components/extension-store/ext-list-item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@
import IconMultiplexer from "@/components/IconMultiplexer.vue"
import { humanReadableNumber } from "@/lib/utils/format"
import { CommandItem } from "~/components/ui/command"
import { CircleCheckBigIcon } from "lucide-vue-next"
import { CircleCheckBigIcon, MoveRightIcon, ArrowRightIcon } from "lucide-vue-next"
import { ExtItem } from "./types"
import {Button} from '@kksh/vue'
import { IconEnum } from "@kksh/api/models"
const props = defineProps<{
data: ExtItem
installedVersion?: string
installed: boolean
upgradeable: boolean
}>()
const emits = defineEmits<{
(e: "select"): void
(e: "upgrade"): void
}>()
function onUpgradeClicked(e: MouseEvent) {
e.stopPropagation()
emits("upgrade")
}
</script>
<template>
<CommandItem :value="props.data" class="" @select="emits('select')">
Expand All @@ -26,10 +36,18 @@ const emits = defineEmits<{
</div>
</div>
</div>
<div class="flex space-x-4">
<div class="flex space-x-2 items-center">
<CircleCheckBigIcon v-if="props.installed" class="w-4 text-green-400" />
<Button v-if="props.upgradeable" class="flex space-x-1 items-center px-2" variant="outline" @click="onUpgradeClicked">
<small>{{ props.installedVersion}} </small>
<MoveRightIcon class="w-4" />
<small>{{ props.data.version }}</small>
</Button>
<small v-else>{{ props.data.version }}</small>
<div class="flex items-center space-x-1">
<Icon name="ic:round-download" class="inline h-5 w-5" />
<Button v-if="!props.installed" variant="ghost" size="icon">
<Icon name="ic:round-download" class="inline h-5 w-5" />
</Button>
<span class="w-8 text-center">{{ humanReadableNumber(props.data.downloads) }}</span>
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion apps/desktop/components/extension-store/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Icon } from "@kksh/api/models"
import { number, object, pipe, string, transform, type InferOutput } from "valibot"
import { number, object, pipe, string, transform, type InferOutput, optional } from "valibot"

export const ExtItemParser = object({
identifier: string(),
name: string(),
downloads: number(),
short_description: string(),
long_description: string(),
version: optional(string()),
api_version: optional(string()),
icon: pipe(
string(),
transform((val) => JSON.parse(val))
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"@pinia-plugin-persistedstate/nuxt": "^1.2.1",
"@tailwindcss/typography": "^0.5.13",
"@types/lodash": "^4.17.7",
"@types/semver": "^7.5.8",
"@types/uuid": "^9.0.7",
"@vue/devtools-api": "^6.6.3",
"release-it": "^17.6.0"
Expand Down
93 changes: 82 additions & 11 deletions apps/desktop/pages/extension-store.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import ExtDrawer from "@/components/extension-store/ExtDrawer.vue"
// import Command from "@/components/extension-store/Command.vue";
import { ExtItem, ExtItemParser } from "@/components/extension-store/types"
import { getExtensionsFolder, SUPABASE_ANON_KEY, SUPABASE_GRAPHQL_ENDPOINT } from "@/lib/constants"
import * as supabase from "@/lib/utils/supabase"
// import { Extension } from "@/lib/extension/ext"
import { gqlClient } from "@/lib/utils/graphql"
import { ApolloClient, gql, HttpLink, InMemoryCache, type ApolloQueryResult } from "@apollo/client"
import type { ExtPackageJsonExtra } from "@kksh/api/models"
import { AllExtensionsDocument, type AllExtensionsQuery } from "@kksh/gql"
import { AllExtensionsDocument, FindLatestExtDocument, type AllExtensionsQuery, type FindLatestExtQuery, type FindLatestExtQueryVariables } from "@kksh/gql"
import { type Tables } from "@kksh/supabase"
import { Button } from "@kksh/vue/button"
import { ArrowLeftIcon } from "@radix-icons/vue"
Expand All @@ -27,6 +28,9 @@ import { useExtStore } from "~/stores/extensionLoader"
import { ElMessage } from "element-plus"
import { parse } from "valibot"
import { onMounted, ref } from "vue"
import { gt } from 'semver'
import { getExtensionFolder } from "@kksh/api/commands"
import { installTarballUrl } from "~/lib/utils/tarball"
const localePath = useLocalePath()
const selectedExt = ref<ExtItem>()
Expand All @@ -41,6 +45,16 @@ function refreshListing() {
})
}
/**
* Map extension identifier to the extension item
*/
const installedExtMap = computed(() => {
return installedManifests.value.reduce((acc, curr) => {
acc[curr.kunkun.identifier] = curr
return acc
}, {} as Record<string, ExtPackageJsonExtra>)
})
onKeyStroke("Escape", () => {
if (document.activeElement?.nodeName === "INPUT") {
navigateTo(localePath("/"))
Expand All @@ -59,14 +73,28 @@ onMounted(async () => {
// console.log(extList.value)
})
/**
* Map extension identifier to the extension item
*/
const extListMap = computed(() => {
return extList.value.reduce((acc, curr) => {
acc[curr.identifier] = curr
return acc
}, {} as Record<string, ExtItem>)
})
function select(item: ExtItem) {
navigateTo(`/store/${item.identifier}`)
selectedExt.value = item
extDrawerOpen.value = true
}
function isInstalled(identifier: string) {
return !!installedManifests.value.find((x) => x.kunkun.identifier === identifier)
// function isInstalled(identifier: string) {
// return !!installedManifests.value.find((x) => x.kunkun.identifier === identifier)
// }
function getInstalledVersion(identifier: string) {
return installedManifests.value.find((x) => x.kunkun.identifier === identifier)?.version
}
function onInstalled(downloads: number) {
Expand Down Expand Up @@ -97,19 +125,59 @@ const filterFunc = (items: ExtItem[], searchTerm: string) => {
})
}
function isUpgradeable(item: ExtItem) {
if (!item.version) return true // latest extensions always have version, this check should be removed later
const installed = installedExtMap.value[item.identifier]
if (!installed) return false
return gt(item.version, installed.version)
}
function upgrade(item: ExtItem) {
console.log(item)
extStore
.uninstallExt(item.identifier)
.then((manifest) => {
ElMessage.success(`Uninstalled: ${manifest.name}`)
extStore.load() // used to refresh isInstalled
})
.then(() => getExtensionFolder())
.then(async (targetInstallDir) => {
console.log("targetInstallDir", targetInstallDir)
if (!targetInstallDir) {
return Promise.reject("Unexpected Error: Extension Folder is Null")
} else {
const response = await gqlClient.query<FindLatestExtQuery, FindLatestExtQueryVariables>({
query: FindLatestExtDocument,
variables: {
identifier: item.identifier
}
})
const exts = response.data.ext_publishCollection?.edges
if (exts && exts.length > 0) {
const tarballUrl = supabase.getFileUrl(exts[0].node.tarball_path).data.publicUrl
return installTarballUrl(tarballUrl, targetInstallDir)
} else {
return Promise.reject("Couldn't find the extension to install from the server")
}
}
})
.then(() => {
ElMessage.success(`Installed: ${item.name}; Version: ${item.version}`)
refreshListing()
})
.catch((err) => {
ElMessage.error(`${err}`)
// error(`Failed to uninstall extension ${extIdentifier}: ${err}`)
})
}
function goBack() {
navigateTo(localePath("/"))
}
</script>
<template>
<div>
<!-- <ExtDrawer
v-model:open="extDrawerOpen"
:selectedExt="selectedExt"
:installed="selectedExt?.identifier ? isInstalled(selectedExt?.identifier) : false"
@installed="onInstalled"
@uninstall="uninstall"
/> -->
<Command :filterFunction="(val, searchTerm) => filterFunc(val as ExtItem[], searchTerm)">
<CommandInput placeholder="Type to search..." class="text-md h-12">
<Button size="icon" variant="outline" @click="goBack">
Expand All @@ -122,7 +190,10 @@ function goBack() {
<ExtListItem
v-for="item in extList"
:data="item"
:installed="isInstalled(item.identifier)"
@upgrade="upgrade(item)"
:upgradeable="isUpgradeable(item)"
:installedVersion="getInstalledVersion(item.identifier)"
:installed="!!getInstalledVersion(item.identifier)"
@select="select(item)"
/>
</CommandGroup>
Expand Down
2 changes: 0 additions & 2 deletions apps/desktop/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ const searchTermSyncProxy = computed({
})
</script>
<template>
<span>{{version}}</span>
<!-- <iframe src="ext://template-ext-sveltekit.build.dev-ext/about" class="w-full h-96" frameborder="0"></iframe> -->
<CmdPaletteCommand
class=""
v-model:searchTerm="searchTermSyncProxy"
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/pages/store/[identifier].vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ const isInstalled = computed(() => {
return extStore.manifests.find((m) => m.kunkun.identifier === extIdentifier) !== undefined
})
// TODO implement install and uninstall
const demoImages = computed(() => {
return currentExt.value?.demo_images.map((src) => supabase.getFileUrl(src).data.publicUrl) ?? []
})
Expand Down
2 changes: 1 addition & 1 deletion packages/api/patch-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const versionTsContent = fs.readFileSync("./src/version.ts", "utf-8")
const lines: string[] = []
for (const line of versionTsContent.split("\n")) {
if (line.includes("export const version")) {
lines.push(`export const version = "${version}"\n`)
lines.push(`export const version = "${version}"`)
} else {
lines.push(line)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/gql/src/operations/allExtensions.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ query AllExtensions {
edges {
node {
identifier
version
api_version
name
downloads
short_description
Expand Down
1 change: 1 addition & 0 deletions packages/gql/src/operations/findLatestExt.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ query FindLatestExt($identifier: String) {
created_at
name
version
api_version
manifest
shasum
size
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 57a65f2

Please sign in to comment.