✨ 添加安装检测和启动应用
This commit is contained in:
@@ -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 其他依赖
|
||||||
|
|||||||
22
src-tauri/src/handlers/deb.rs
Normal file
22
src-tauri/src/handlers/deb.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentApp.Filename) {
|
return (
|
||||||
showToast({ description: '获取应用下载信息失败', variant: "error" });
|
<Button
|
||||||
return;
|
size="lg"
|
||||||
}
|
class='w-full'
|
||||||
|
onClick={() => {
|
||||||
|
const currentApp = app();
|
||||||
|
if (!currentApp) {
|
||||||
|
showToast({ description: '获取应用信息失败', variant: "error" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
addDownload(params.category, params.pkgname, currentApp.Filename, currentApp.Name || '');
|
if (!currentApp.Filename) {
|
||||||
showToast({ description: '已添加到下载队列', variant: "success" });
|
showToast({ description: '获取应用下载信息失败', variant: "error" });
|
||||||
}}
|
return;
|
||||||
>
|
}
|
||||||
安装
|
|
||||||
</Button>
|
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
9
src/lib/api/deb.ts
Normal 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 });
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user