添加安装检测和启动应用

This commit is contained in:
柚子
2025-03-07 02:05:30 +08:00
parent f9a6fcbf84
commit 135873a3e7
6 changed files with 111 additions and 50 deletions

View File

@@ -5,3 +5,4 @@ This template should help get you started developing with Tauri, Solid and Types
## Recommended IDE Setup ## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) - [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
##### 1.2 其他依赖

View File

@@ -0,0 +1,22 @@
use std::process::Command;
#[tauri::command]
pub async fn check_is_installed(pkgname: String) -> Result<bool, String> {
let output = Command::new("/opt/durapps/spark-store/bin/store-helper/check-is-installed")
.arg(&pkgname)
.output()
.map_err(|e| format!("执行命令失败: {}", e))?;
Ok(output.status.success())
}
#[tauri::command]
pub async fn launch_app(pkgname: String) -> Result<(), String> {
Command::new("/opt/durapps/spark-store/bin/store-helper/ss-launcher")
.arg(&pkgname)
.spawn()
.map_err(|e| format!("启动应用失败: {}", e))?;
Ok(())
}

View File

@@ -4,3 +4,4 @@ pub mod app;
pub mod home; pub mod home;
pub mod file; pub mod file;
pub mod download; pub mod download;
pub mod deb;

View File

@@ -30,6 +30,8 @@ pub fn run() {
handlers::download::pause_download, handlers::download::pause_download,
handlers::download::resume_download, handlers::download::resume_download,
handlers::download::cancel_download, handlers::download::cancel_download,
handlers::deb::check_is_installed,
handlers::deb::launch_app,
utils::get_user_agent, utils::get_user_agent,
]) ])
.on_window_event(|window, event| match event { .on_window_event(|window, event| match event {

View File

@@ -1,4 +1,4 @@
import { Component, createSignal } from 'solid-js'; import { Component, createSignal, onMount } from 'solid-js';
import { useParams } from '@solidjs/router'; import { useParams } from '@solidjs/router';
import { useAppDetailStore } from './store'; import { useAppDetailStore } from './store';
import './AppDetail.css'; import './AppDetail.css';
@@ -16,6 +16,7 @@ import { useCollectionStore } from '@/features/collection/store';
import { useDownloadsStore } from '@/features/downloads/store'; import { useDownloadsStore } from '@/features/downloads/store';
import { Progress } from '@/components/ui/progress'; import { Progress } from '@/components/ui/progress';
import { X } from 'lucide-solid'; import { X } from 'lucide-solid';
import { checkIsInstalled, launchApp } from '@/lib/api/deb';
const AppDetail: Component = () => { const AppDetail: Component = () => {
const params = useParams(); const params = useParams();
@@ -26,6 +27,7 @@ const AppDetail: Component = () => {
const [newCollectionDesc, setNewCollectionDesc] = createSignal(''); const [newCollectionDesc, setNewCollectionDesc] = createSignal('');
const [showNewCollectionDialog, setShowNewCollectionDialog] = createSignal(false); const [showNewCollectionDialog, setShowNewCollectionDialog] = createSignal(false);
const [showCollectionDialog, setShowCollectionDialog] = createSignal(false); const [showCollectionDialog, setShowCollectionDialog] = createSignal(false);
const [isInstalled, setIsInstalled] = createSignal(false);
const [selectedCollections, setSelectedCollections] = createSignal<string[]>([]); const [selectedCollections, setSelectedCollections] = createSignal<string[]>([]);
const handleCreateCollection = () => { const handleCreateCollection = () => {
@@ -45,6 +47,11 @@ const AppDetail: Component = () => {
showToast({ description: '收藏单创建成功', variant: "success" }); showToast({ description: '收藏单创建成功', variant: "success" });
}; };
onMount(async () => {
const installed = await checkIsInstalled(params.pkgname);
setIsInstalled(installed);
});
return ( return (
<div class="w-full h-full"> <div class="w-full h-full">
{loading() ? ( {loading() ? (
@@ -110,56 +117,75 @@ const AppDetail: Component = () => {
<h2 class="app-name text-2xl font-bold pt-2">{app()?.Name}</h2> <h2 class="app-name text-2xl font-bold pt-2">{app()?.Name}</h2>
</div> </div>
<div class="flex flex-col gap-2 w-[120px]"> <div class="flex flex-col gap-2 w-[120px]">
{downloads().some(task => task.category === params.category && task.pkgname === params.pkgname) ? ( {(() => {
<div class="flex flex-col gap-2 w-full pb-2"> const downloadTask = downloads().find(task =>
{downloads().map(download => { task.category === params.category &&
if (download.category === params.category && download.pkgname === params.pkgname) { task.pkgname === params.pkgname
return ( );
<div class="flex items-center gap-2 group relative">
<Progress value={download.progress} class="flex-1" /> if (downloadTask?.status === 'installed' || isInstalled()) {
<div class="flex items-center gap-2 group-hover:opacity-0 transition-opacity"> return (
<span class="text-sm text-muted-foreground">{download.progress}%</span> <Button
{download.speed && ( size="lg"
<span class="text-sm text-muted-foreground">{download.speed}</span> class='w-full'
)} onClick={() => {
</div> launchApp(params.pkgname)
<button showToast({ description: '正在启动应用...' });
class="absolute right-1 bg-red-500 rounded-full w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center text-white hover:bg-red-600" }}
onClick={() => { >
cancelDownload(params.category, params.pkgname);
}} </Button>
> );
<X /> }
</button>
if (downloadTask) {
return (
<div class="flex flex-col gap-2 w-full pb-2">
<div class="flex items-center gap-2 group relative">
<Progress value={downloadTask.progress} class="flex-1" />
<div class="flex items-center gap-2 group-hover:opacity-0 transition-opacity">
<span class="text-sm text-muted-foreground">{downloadTask.progress}%</span>
{downloadTask.speed && (
<span class="text-sm text-muted-foreground">{downloadTask.speed}</span>
)}
</div> </div>
); <button
} class="absolute right-1 bg-red-500 rounded-full w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center text-white hover:bg-red-600"
return null; onClick={() => {
})} cancelDownload(params.category, params.pkgname);
</div> }}
) : ( >
<Button <X />
size="lg" </button>
class='w-full' </div>
onClick={() => { </div>
const currentApp = app(); );
if (!currentApp) { }
showToast({ description: '获取应用信息失败', variant: "error" });
return; return (
} <Button
size="lg"
if (!currentApp.Filename) { class='w-full'
showToast({ description: '获取应用下载信息失败', variant: "error" }); onClick={() => {
return; const currentApp = app();
} if (!currentApp) {
showToast({ description: '获取应用信息失败', variant: "error" });
addDownload(params.category, params.pkgname, currentApp.Filename, currentApp.Name || ''); return;
showToast({ description: '已添加到下载队列', variant: "success" }); }
}}
> if (!currentApp.Filename) {
showToast({ description: '获取应用下载信息失败', variant: "error" });
</Button> return;
)} }
addDownload(params.category, params.pkgname, currentApp.Filename, currentApp.Name || '');
showToast({ description: '已添加到下载队列', variant: "success" });
}}
>
</Button>
);
})()}
<Dialog open={showCollectionDialog()} onOpenChange={setShowCollectionDialog}> <Dialog open={showCollectionDialog()} onOpenChange={setShowCollectionDialog}>
<DialogTrigger> <DialogTrigger>
<Button variant="secondary" size="lg" class='w-full'></Button> <Button variant="secondary" size="lg" class='w-full'></Button>

9
src/lib/api/deb.ts Normal file
View File

@@ -0,0 +1,9 @@
import { invoke } from "@tauri-apps/api/core";
export async function checkIsInstalled(pkgname: string): Promise<boolean> {
return await invoke('check_is_installed', { pkgname });
}
export async function launchApp(pkgname: string) {
await invoke('launch_app', { pkgname });
}