Skip to content

Commit

Permalink
feat: implement extension upgrade check
Browse files Browse the repository at this point in the history
Checks once a day, when there is extension upgradeable, notify user to go to store to upgrade. Auto upgrade is not yet implemented.
  • Loading branch information
HuakunShen committed Sep 13, 2024
1 parent 7853118 commit ff0fa51
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 70 deletions.
4 changes: 0 additions & 4 deletions apps/desktop/components/ExtTemplate/ListView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,8 @@ function filterFunction(items: ListSchema.Item[], searchTerm: string) {
}
function onHighlightedItemChanged(itemValue: string) {
console.log("onHighlightedItemChanged", itemValue)
console.log("props.modelValue.defaultAction", props.modelValue.defaultAction)
props.workerAPI?.onHighlightedListItemChanged(itemValue)
const item = props.modelValue.items?.find((item) => item.value === itemValue)
console.log("item", item)
// if an action or defaultAction is set in List directly, the item's action or defaultAction will be overridden
if (props.modelValue.actions) {
appUiStore.setActionPanel(props.modelValue.actions)
Expand Down
21 changes: 2 additions & 19 deletions apps/desktop/components/settings/AboutTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import { cn } from "@/lib/utils"
import { Button } from "@kksh/vue/button"
import { Card, CardContent } from "@kksh/vue/card"
import { getVersion } from "@tauri-apps/api/app"
import { confirm } from "@tauri-apps/plugin-dialog"
import { relaunch } from "@tauri-apps/plugin-process"
import { check } from "@tauri-apps/plugin-updater"
import { ElMessage, ElNotification } from "element-plus"
import { onMounted, ref, type HTMLAttributes } from "vue"
import { checkUpdateAndInstall } from "@/lib/utils/updater"
const appVersion = ref("")
onMounted(() => {
Expand All @@ -20,20 +17,6 @@ const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
async function checkUpdate() {
const update = await check()
if (update?.available) {
const confirmUpdate = await confirm(
`A new version ${update.version} is available. Do you want to install and relaunch?`
)
if (confirmUpdate) {
await update.downloadAndInstall()
await relaunch()
}
} else {
ElNotification.info("You are on the latest version")
}
}
</script>
<template>
<Card :class="cn('flex h-full items-center justify-center border-none', props.class)">
Expand All @@ -57,7 +40,7 @@ async function checkUpdate() {
{{ $t("extensionsSourceCode") }}
<Icon name="mdi:github" class="inline -translate-y-0.5 text-white" />
</TauriLink>
<Button @click="checkUpdate" size="xs" variant="secondary">{{ $t("checkUpdate") }}</Button>
<Button @click="checkUpdateAndInstall" size="xs" variant="secondary">{{ $t("checkUpdate") }}</Button>
</div>
</CardContent>
</Card>
Expand Down
53 changes: 53 additions & 0 deletions apps/desktop/lib/utils/updater.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { confirm } from "@tauri-apps/plugin-dialog"
import { relaunch } from "@tauri-apps/plugin-process"
import { check } from "@tauri-apps/plugin-updater"
import { ElMessage, ElNotification } from "element-plus"
import { gqlClient } from "@/lib/utils/graphql"
import { FindLatestExtDocument, type FindLatestExtQuery, type FindLatestExtQueryVariables } from "@kksh/gql"
import {gt} from 'semver'
import type { ExtPackageJsonExtra } from "@kksh/api/models"


export async function checkUpdateAndInstall() {
const update = await check()
if (update?.available) {
const confirmUpdate = await confirm(
`A new version ${update.version} is available. Do you want to install and relaunch?`
)
if (confirmUpdate) {
await update.downloadAndInstall()
await relaunch()
}
} else {
ElNotification.info("You are on the latest version")
}
}

export async function checkSingleExtensionUpdate(installedExt: ExtPackageJsonExtra) {
const extStore = useExtStore()
const response = await gqlClient.query<FindLatestExtQuery, FindLatestExtQueryVariables>({
query: FindLatestExtDocument,
variables: {
identifier: installedExt.kunkun.identifier
}
})
const exts = response.data.ext_publishCollection?.edges
console.log(exts);
if (!exts || exts?.length <= 0) {
return
}
const ext = exts[0].node
if (gt(ext.version, installedExt.version)) {
console.log(`new version available ${installedExt.kunkun.identifier} ${ext.version}`);
ElNotification.info(`Extension ${installedExt.kunkun.identifier} has a new version ${ext.version}, you can upgrade in Store.`)
}
}

export async function checkExtensionUpdate() {
console.log("checkExtensionUpdate");

const extStore = useExtStore()
extStore.manifests.forEach(async (ext) => {
await checkSingleExtensionUpdate(ext)
})
}
19 changes: 8 additions & 11 deletions apps/desktop/pages/extension-store.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { onMounted, ref } from "vue"
import { gt } from 'semver'
import { getExtensionFolder } from "@kksh/api/commands"
import { installTarballUrl } from "~/lib/utils/tarball"
import { isCompatible } from '@kksh/api'
const localePath = useLocalePath()
const selectedExt = ref<ExtItem>()
Expand Down Expand Up @@ -70,7 +71,10 @@ onMounted(async () => {
response.data.extensionsCollection?.edges.map((x) =>
parse(ExtItem, parse(ExtItemParser, x.node))
) ?? []
// console.log(extList.value)
})
const sortedExtList = computed(() => {
return extList.value.sort((a, b) => isUpgradeable(b) ? 1 : isUpgradeable(a) ? -1 : 0)
})
/**
Expand Down Expand Up @@ -129,21 +133,14 @@ 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)
return gt(item.version, installed.version) && (item.api_version ? isCompatible(item.api_version) : true)
}
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 {
Expand All @@ -163,7 +160,7 @@ function upgrade(item: ExtItem) {
}
})
.then(() => {
ElMessage.success(`Installed: ${item.name}; Version: ${item.version}`)
ElMessage.success(`Upgraded: ${item.name}; Version: ${item.version}`)
refreshListing()
})
.catch((err) => {
Expand All @@ -188,7 +185,7 @@ function goBack() {
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Extensions">
<ExtListItem
v-for="item in extList"
v-for="item in sortedExtList"
:data="item"
@upgrade="upgrade(item)"
:upgradeable="isUpgradeable(item)"
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { useSystemCmdsStore } from "~/stores/systemCmds"
import { ComboboxInput } from "radix-vue"
import { toast } from "vue-sonner"
import {version} from '@kksh/api/package.json'
import { useLastTimeStore } from "~/stores/lastTime"
import { checkExtensionUpdate, checkUpdateAndInstall } from "~/lib/utils/updater"
const builtinCmdStore = useBuiltInCmdStore()
const appsStore = useAppsLoaderStore()
Expand All @@ -29,6 +31,8 @@ const extStore = useExtStore()
const searchTermSync = useStore($searchTermSync)
const appConfig = useAppConfigStore()
await appConfig.init()
const lastTimeStore = useLastTimeStore()
await lastTimeStore.init()
const extLoaders = ref([
devExtStore,
extStore,
Expand Down Expand Up @@ -92,6 +96,10 @@ onMounted(async () => {
if (platform() !== "macos") {
appWindow.setDecorations(false)
}
if (lastTimeStore.expired()) {
checkUpdateAndInstall()
checkExtensionUpdate()
}
appWindow.show()
// force rerender groups
const cache = extLoaders.value
Expand Down
35 changes: 0 additions & 35 deletions apps/desktop/stores/appConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,41 +46,6 @@ export const appConfigSchema = object({
})
type State = InferOutput<typeof appConfigSchema>

// function isAppConfigEqual(a: State, b: State) {
// if (a.isInitialized !== b.isInitialized) {
// return false
// }
// if (a.theme !== b.theme) {
// return false
// }
// if (a.radius !== b.radius) {
// return false
// }
// if (a.triggerHotkey !== b.triggerHotkey) {
// return false
// }
// if (a.lightMode !== b.lightMode) {
// return false
// }
// if (a.launchAtLogin !== b.launchAtLogin) {
// return false
// }
// if (a.showInTray !== b.showInTray) {
// return false
// }
// if (a.devExtensionPath != b.devExtensionPath) {
// console.log("devExtensionPath unequal", a.devExtensionPath, b.devExtensionPath);
// return false
// }
// if (a.devExtLoadUrl !== b.devExtLoadUrl) {
// return false
// }
// if (a.hideOnBlur !== b.hideOnBlur) {
// return false
// }
// return true
// }

export const useAppConfigStore = defineStore("appConfig", {
state: (): State => ({
isInitialized: false,
Expand Down
9 changes: 9 additions & 0 deletions apps/desktop/stores/builtinCmdLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { defineStore } from "pinia"
import { v4 as uuidv4 } from "uuid"
import { toast } from "vue-sonner"
import { useAppStateStore } from "./appState"
import { checkUpdateAndInstall } from "~/lib/utils/updater"

const localePath = useLocalePath()
const rtConfig = useRuntimeConfig()
Expand Down Expand Up @@ -53,6 +54,14 @@ const builtinCmds: BuiltinCmd[] = [
$searchTermSync.set("")
}
},
{
name: "Check Update",
iconifyIcon: "material-symbols:update",
description: "Check for updates",
function: async () => {
checkUpdateAndInstall()
}
},
{
name: "Reload",
iconifyIcon: "tabler:reload",
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/stores/extensionLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ export function constructExtStore(options: { isDev: boolean }) {
return Promise.resolve()
}

function findExtByIdentifier(identifier: string) {
return manifests.value.find((m) => m.kunkun.identifier === identifier)
}

return {
id: uuidv4(),
extensionName,
Expand All @@ -312,6 +316,7 @@ export function constructExtStore(options: { isDev: boolean }) {
extPath,
setExtPath,
manifests,
findExtByIdentifier,
$listItems,
$filteredListItems,
uninstallExt,
Expand Down
33 changes: 33 additions & 0 deletions apps/desktop/stores/lastTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { defineStore } from 'pinia'
import { Store } from "@tauri-apps/plugin-store"
import * as v from 'valibot'

const persistAppConfig = new Store("appConfig.bin")

export const useLastTimeStore = defineStore('kk-last-time', {
state: () => ({
lastCheckUpdateTime: new Date(),
}),
actions: {
elapsedTime() {
const now = new Date()
return now.getTime() - this.lastCheckUpdateTime.getTime()
},
expired() {
// check update every 24 hours
return this.elapsedTime() > 1000 * 60 * 60 * 24
},
async init() {
const lastCheckUpdateTime = await persistAppConfig.get("lastCheckUpdateTime")
if (lastCheckUpdateTime) {
this.lastCheckUpdateTime = new Date(v.parse(v.string(), lastCheckUpdateTime))
} else {
await this.update()
}
},
update() {
this.lastCheckUpdateTime = new Date()
persistAppConfig.set("lastCheckUpdateTime", this.lastCheckUpdateTime.toISOString())
}
},
})
2 changes: 1 addition & 1 deletion packages/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { version, breakingChangesVersionCheckpoints } from "./version"
export { version, breakingChangesVersionCheckpoints, isVersionBetween, isCompatible } from "./version"

0 comments on commit ff0fa51

Please sign in to comment.