mirror of
https://github.com/vrc-get/vrc-get.git
synced 2026-06-21 09:58:08 +00:00
fix(gui): install / upgrade package
This commit is contained in:
parent
730a5f95fd
commit
815b8c9cfd
6 changed files with 447 additions and 45 deletions
|
|
@ -3,6 +3,7 @@ use std::io;
|
|||
use std::num::Wrapping;
|
||||
use std::path::PathBuf;
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use serde::Serialize;
|
||||
use specta::specta;
|
||||
|
|
@ -10,19 +11,24 @@ use tauri::async_runtime::Mutex;
|
|||
use tauri::{generate_handler, Invoke, Runtime, State};
|
||||
|
||||
use vrc_get_vpm::environment::UserProject;
|
||||
use vrc_get_vpm::io::DefaultProjectIo;
|
||||
use vrc_get_vpm::unity_project::pending_project_changes::{
|
||||
ConflictInfo, PackageChange, RemoveReason,
|
||||
};
|
||||
use vrc_get_vpm::unity_project::{AddPackageOperation, PendingProjectChanges};
|
||||
use vrc_get_vpm::version::Version;
|
||||
use vrc_get_vpm::{PackageCollection, PackageInfo, PackageJson, ProjectType, UnityProject};
|
||||
use vrc_get_vpm::{PackageCollection, PackageInfo, PackageJson, ProjectType};
|
||||
|
||||
pub(crate) fn handlers<R: Runtime>() -> impl Fn(Invoke<R>) + Send + Sync + 'static {
|
||||
generate_handler![
|
||||
environment_projects,
|
||||
environment_packages,
|
||||
project_details,
|
||||
environment_repositories_info,
|
||||
environment_hide_repository,
|
||||
environment_show_repository,
|
||||
environment_set_hide_local_user_packages,
|
||||
project_details,
|
||||
project_install_package,
|
||||
project_apply_pending_changes,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -32,11 +38,13 @@ pub(crate) fn export_ts() {
|
|||
specta::collect_types![
|
||||
environment_projects,
|
||||
environment_packages,
|
||||
project_details,
|
||||
environment_repositories_info,
|
||||
environment_hide_repository,
|
||||
environment_show_repository,
|
||||
environment_set_hide_local_user_packages,
|
||||
project_details,
|
||||
project_install_package,
|
||||
project_apply_pending_changes,
|
||||
]
|
||||
.unwrap(),
|
||||
specta::ts::ExportConfiguration::new().bigint(specta::ts::BigIntExportBehavior::Number),
|
||||
|
|
@ -50,6 +58,7 @@ pub(crate) fn new_env_state() -> impl Send + Sync + 'static {
|
|||
}
|
||||
|
||||
type Environment = vrc_get_vpm::Environment<reqwest::Client, vrc_get_vpm::io::DefaultEnvironmentIo>;
|
||||
type UnityProject = vrc_get_vpm::UnityProject<vrc_get_vpm::io::DefaultProjectIo>;
|
||||
|
||||
async fn new_environment() -> io::Result<Environment> {
|
||||
let client = reqwest::Client::new();
|
||||
|
|
@ -79,6 +88,15 @@ struct EnvironmentState {
|
|||
// null or reference to
|
||||
projects: Box<[UserProject]>,
|
||||
projects_version: Wrapping<u32>,
|
||||
changes_info: Option<NonNull<PendingProjectChangesInfo<'static>>>,
|
||||
}
|
||||
|
||||
static CHANGES_GLOBAL_INDEXER: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
struct PendingProjectChangesInfo<'env> {
|
||||
environment_version: u32,
|
||||
changes_version: u32,
|
||||
changes: PendingProjectChanges<'env>,
|
||||
}
|
||||
|
||||
struct EnvironmentHolder {
|
||||
|
|
@ -118,6 +136,7 @@ impl EnvironmentState {
|
|||
packages: None,
|
||||
projects: Box::new([]),
|
||||
projects_version: Wrapping(0),
|
||||
changes_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -314,6 +333,7 @@ async fn environment_packages(
|
|||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
if let Some(ptr) = env_state.packages {
|
||||
env_state.packages = None; // avoid a double drop
|
||||
unsafe { drop(Box::from_raw(ptr.as_ptr())) }
|
||||
}
|
||||
env_state.packages = NonNull::new(Box::into_raw(packages) as *mut _);
|
||||
|
|
@ -421,11 +441,17 @@ struct TauriProjectDetails {
|
|||
installed_packages: Vec<(String, TauriBasePackageInfo)>,
|
||||
}
|
||||
|
||||
async fn load_project(project_path: String) -> Result<UnityProject, RustError> {
|
||||
Ok(UnityProject::load(vrc_get_vpm::io::DefaultProjectIo::new(
|
||||
PathBuf::from(project_path).into(),
|
||||
))
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
async fn project_details(project_path: String) -> Result<TauriProjectDetails, RustError> {
|
||||
let unity_project =
|
||||
UnityProject::load(DefaultProjectIo::new(PathBuf::from(project_path).into())).await?;
|
||||
let unity_project = load_project(project_path).await?;
|
||||
|
||||
Ok(TauriProjectDetails {
|
||||
unity: unity_project
|
||||
|
|
@ -441,3 +467,188 @@ async fn project_details(project_path: String) -> Result<TauriProjectDetails, Ru
|
|||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize, specta::Type)]
|
||||
struct TauriPendingProjectChanges {
|
||||
changes_version: u32,
|
||||
package_changes: Vec<(String, TauriPackageChange)>,
|
||||
|
||||
remove_legacy_files: Vec<String>,
|
||||
remove_legacy_folders: Vec<String>,
|
||||
|
||||
conflicts: Vec<(String, TauriConflictInfo)>,
|
||||
}
|
||||
|
||||
impl TauriPendingProjectChanges {
|
||||
fn new(version: u32, changes: &PendingProjectChanges) -> Self {
|
||||
TauriPendingProjectChanges {
|
||||
changes_version: version,
|
||||
package_changes: changes
|
||||
.package_changes()
|
||||
.iter()
|
||||
.filter_map(|(name, change)| Some((name.to_string(), change.try_into().ok()?)))
|
||||
.collect(),
|
||||
remove_legacy_files: changes
|
||||
.remove_legacy_files()
|
||||
.iter()
|
||||
.map(|x| x.to_string_lossy().into_owned())
|
||||
.collect(),
|
||||
remove_legacy_folders: changes
|
||||
.remove_legacy_folders()
|
||||
.iter()
|
||||
.map(|x| x.to_string_lossy().into_owned())
|
||||
.collect(),
|
||||
conflicts: changes
|
||||
.conflicts()
|
||||
.iter()
|
||||
.map(|(name, info)| (name.to_string(), info.into()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, specta::Type)]
|
||||
enum TauriPackageChange {
|
||||
InstallNew(TauriBasePackageInfo),
|
||||
Remove(TauriRemoveReason),
|
||||
}
|
||||
|
||||
impl TryFrom<&PackageChange<'_>> for TauriPackageChange {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &PackageChange) -> Result<Self, ()> {
|
||||
Ok(match value {
|
||||
PackageChange::Install(install) => TauriPackageChange::InstallNew(
|
||||
TauriBasePackageInfo::new(install.install_package().ok_or(())?.package_json()),
|
||||
),
|
||||
PackageChange::Remove(remove) => TauriPackageChange::Remove(remove.reason().into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, specta::Type)]
|
||||
enum TauriRemoveReason {
|
||||
Requested,
|
||||
Legacy,
|
||||
Unused,
|
||||
}
|
||||
|
||||
impl From<RemoveReason> for TauriRemoveReason {
|
||||
fn from(value: RemoveReason) -> Self {
|
||||
match value {
|
||||
RemoveReason::Requested => Self::Requested,
|
||||
RemoveReason::Legacy => Self::Legacy,
|
||||
RemoveReason::Unused => Self::Unused,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, specta::Type)]
|
||||
struct TauriConflictInfo {
|
||||
packages: Vec<String>,
|
||||
unity_conflict: bool,
|
||||
}
|
||||
|
||||
impl From<&ConflictInfo> for TauriConflictInfo {
|
||||
fn from(value: &ConflictInfo) -> Self {
|
||||
Self {
|
||||
packages: value
|
||||
.conflicting_packages()
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect(),
|
||||
unity_conflict: value.conflicts_with_unity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
async fn project_install_package(
|
||||
state: State<'_, Mutex<EnvironmentState>>,
|
||||
project_path: String,
|
||||
env_version: u32,
|
||||
package_index: usize,
|
||||
) -> Result<TauriPendingProjectChanges, RustError> {
|
||||
let mut env_state = state.lock().await;
|
||||
let env_state = &mut *env_state;
|
||||
if env_state.environment.environment_version != Wrapping(env_version) {
|
||||
return Err(RustError::Unrecoverable(
|
||||
"environment version mismatch".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let environment = env_state.environment.get_environment_mut(false).await?;
|
||||
let packages = unsafe { &*env_state.packages.unwrap().as_mut() };
|
||||
let installing_package = packages[package_index];
|
||||
|
||||
let unity_project = load_project(project_path).await?;
|
||||
|
||||
let operation = if let Some(locked) = unity_project.get_locked(installing_package.name()) {
|
||||
if installing_package.version() < locked.version() {
|
||||
AddPackageOperation::Downgrade
|
||||
} else {
|
||||
AddPackageOperation::UpgradeLocked
|
||||
}
|
||||
} else {
|
||||
AddPackageOperation::InstallToDependencies
|
||||
};
|
||||
|
||||
let changes = match unity_project
|
||||
.add_package_request(environment, vec![installing_package], operation, false)
|
||||
.await
|
||||
{
|
||||
Ok(request) => request,
|
||||
Err(e) => return Err(RustError::Unrecoverable(format!("{e}"))),
|
||||
};
|
||||
|
||||
let new_version = CHANGES_GLOBAL_INDEXER.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
let result = TauriPendingProjectChanges::new(new_version, &changes);
|
||||
|
||||
let changes_info = Box::new(PendingProjectChangesInfo {
|
||||
environment_version: env_version,
|
||||
changes_version: new_version,
|
||||
changes,
|
||||
});
|
||||
|
||||
if let Some(ptr) = env_state.changes_info {
|
||||
env_state.changes_info = None;
|
||||
unsafe { drop(Box::from_raw(ptr.as_ptr())) }
|
||||
}
|
||||
env_state.changes_info = NonNull::new(Box::into_raw(changes_info) as *mut _);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
async fn project_apply_pending_changes(
|
||||
state: State<'_, Mutex<EnvironmentState>>,
|
||||
project_path: String,
|
||||
changes_version: u32,
|
||||
) -> Result<(), RustError> {
|
||||
let mut env_state = state.lock().await;
|
||||
let env_state = &mut *env_state;
|
||||
let changes = unsafe { Box::from_raw(env_state.changes_info.take().unwrap().as_mut()) };
|
||||
if changes.changes_version != changes_version {
|
||||
return Err(RustError::Unrecoverable("changes version mismatch".into()));
|
||||
}
|
||||
let changes = *changes;
|
||||
if changes.environment_version != env_state.environment.environment_version.0 {
|
||||
return Err(RustError::Unrecoverable(
|
||||
"environment version mismatch".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let environment = env_state.environment.get_environment_mut(false).await?;
|
||||
|
||||
let mut unity_project = load_project(project_path).await?;
|
||||
|
||||
unity_project
|
||||
.apply_pending_changes(environment, changes.changes)
|
||||
.await?;
|
||||
|
||||
unity_project.save().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import {
|
|||
Button,
|
||||
ButtonGroup,
|
||||
Card,
|
||||
Checkbox,
|
||||
IconButton,
|
||||
Checkbox, Dialog, DialogBody, DialogFooter, DialogHeader,
|
||||
IconButton, List, ListItem,
|
||||
Menu,
|
||||
MenuHandler,
|
||||
MenuItem,
|
||||
|
|
@ -16,7 +16,7 @@ import {
|
|||
Tooltip,
|
||||
Typography
|
||||
} from "@material-tailwind/react";
|
||||
import React, {Suspense, useMemo} from "react";
|
||||
import React, {Suspense, useMemo, useState} from "react";
|
||||
import {ArrowLeftIcon, ArrowPathIcon, ChevronDownIcon,} from "@heroicons/react/24/solid";
|
||||
import {MinusCircleIcon, PlusCircleIcon,} from "@heroicons/react/24/outline";
|
||||
import {HNavBar, VStack} from "@/components/layout";
|
||||
|
|
@ -28,19 +28,31 @@ import {
|
|||
environmentPackages,
|
||||
environmentRepositoriesInfo,
|
||||
environmentSetHideLocalUserPackages,
|
||||
environmentShowRepository,
|
||||
projectDetails,
|
||||
environmentShowRepository, projectApplyPendingChanges,
|
||||
projectDetails, projectInstallPackage,
|
||||
TauriBasePackageInfo,
|
||||
TauriPackage,
|
||||
TauriPackage, TauriPendingProjectChanges,
|
||||
TauriProjectDetails,
|
||||
TauriVersion
|
||||
} from "@/lib/bindings";
|
||||
import {compareUnityVersion, compareVersion, toVersionString} from "@/lib/version";
|
||||
import {createWatcher} from "tailwindcss/src/oxide/cli/build/watching";
|
||||
|
||||
export default function Page(props: {}) {
|
||||
return <Suspense><PageBody {...props}/></Suspense>
|
||||
}
|
||||
|
||||
type InstallStatus = {
|
||||
status: "normal";
|
||||
} | {
|
||||
status: "creatingChanges";
|
||||
} | {
|
||||
status: "promptingChanges";
|
||||
changes: TauriPendingProjectChanges;
|
||||
} | {
|
||||
status: "applyingChanges";
|
||||
}
|
||||
|
||||
function PageBody() {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
|
|
@ -72,6 +84,8 @@ function PageBody() {
|
|||
]
|
||||
});
|
||||
|
||||
const [installStatus, setInstallStatus] = useState<InstallStatus>({status: "normal"});
|
||||
|
||||
const packageRows = useMemo(() => {
|
||||
const packages = packagesResult.status == 'success' ? packagesResult.data : [];
|
||||
const details = detailsResult.status == 'success' ? detailsResult.data : null;
|
||||
|
|
@ -102,7 +116,36 @@ function PageBody() {
|
|||
repositoriesInfo.refetch();
|
||||
};
|
||||
|
||||
const isLoading = packagesResult.isFetching || detailsResult.isFetching || repositoriesInfo.isFetching;
|
||||
const onInstallRequested = async (pkg: TauriPackage) => {
|
||||
try {
|
||||
setInstallStatus({status: "creatingChanges"});
|
||||
console.log("install", pkg.name, pkg.version);
|
||||
const changes = await projectInstallPackage(projectPath, pkg.env_version, pkg.index);
|
||||
setInstallStatus({status: "promptingChanges", changes});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setInstallStatus({status: "normal"});
|
||||
}
|
||||
}
|
||||
|
||||
const onRemoveRequested = (pkgId: string) => {
|
||||
console.log("remove", pkgId);
|
||||
}
|
||||
|
||||
const applyChanges = async (changes: TauriPendingProjectChanges) => {
|
||||
try {
|
||||
setInstallStatus({status: "applyingChanges"});
|
||||
await projectApplyPendingChanges(projectPath, changes.changes_version);
|
||||
setInstallStatus({status: "normal"});
|
||||
detailsResult.refetch();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setInstallStatus({status: "normal"});
|
||||
}
|
||||
}
|
||||
|
||||
const installingPackage = installStatus.status != "normal";
|
||||
const isLoading = packagesResult.isFetching || detailsResult.isFetching || repositoriesInfo.isFetching || installingPackage;
|
||||
|
||||
return (
|
||||
<VStack className={"m-4"}>
|
||||
|
|
@ -186,16 +229,129 @@ function PageBody() {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{packageRows.map((row) => (<PackageRow pkg={row} key={row.id}/>))}
|
||||
{packageRows.map((row) => (
|
||||
<PackageRow pkg={row} key={row.id}
|
||||
locked={installingPackage}
|
||||
onInstallRequested={onInstallRequested}
|
||||
onRemoveRequested={onRemoveRequested}/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</Card>
|
||||
{
|
||||
installStatus.status === "promptingChanges" ? (
|
||||
<ProjectChangesDialog changes={installStatus.changes}
|
||||
cancel={() => setInstallStatus({status: "normal"})}
|
||||
apply={() => applyChanges(installStatus.changes)}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
</main>
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
|
||||
function ProjectChangesDialog(
|
||||
{
|
||||
changes,
|
||||
cancel,
|
||||
apply,
|
||||
}: {
|
||||
changes: TauriPendingProjectChanges,
|
||||
cancel: () => void,
|
||||
apply: () => void,
|
||||
}) {
|
||||
const versionConflicts = changes.conflicts.filter(([_, c]) => c.packages.length > 0);
|
||||
const unityConflicts = changes.conflicts.filter(([_, c]) => c.unity_conflict);
|
||||
|
||||
return (
|
||||
<Dialog open handler={() => {}}>
|
||||
<DialogHeader>Apply Changes</DialogHeader>
|
||||
<DialogBody>
|
||||
<Typography className={"text-gray-900"}>
|
||||
You're applying the following changes to the project
|
||||
</Typography>
|
||||
<List>
|
||||
{changes.package_changes.map(([pkgId, pkgChange]) => {
|
||||
if ('InstallNew' in pkgChange) {
|
||||
return <ListItem key={pkgId}>
|
||||
Install {pkgChange.InstallNew.display_name ?? pkgChange.InstallNew.name} version {toVersionString(pkgChange.InstallNew.version)}
|
||||
</ListItem>
|
||||
} else {
|
||||
switch (pkgChange.Remove) {
|
||||
case "Requested":
|
||||
return <ListItem key={pkgId}>Remove {pkgId} since you requested.</ListItem>
|
||||
case "Legacy":
|
||||
return <ListItem key={pkgId}>Remove {pkgId} since it's a legacy package.</ListItem>
|
||||
case "Unused":
|
||||
return <ListItem key={pkgId}>Remove {pkgId} since it's unused.</ListItem>
|
||||
}
|
||||
}
|
||||
})}
|
||||
</List>
|
||||
{
|
||||
versionConflicts.length > 0 ? (
|
||||
<>
|
||||
<Typography className={"text-red-700"}>
|
||||
There are version conflicts
|
||||
</Typography>
|
||||
<List>
|
||||
{versionConflicts.map(([pkgId, conflict]) => (
|
||||
<ListItem key={pkgId}>
|
||||
{pkgId} conflicts with {conflict.packages.map(p => p).join(", ")}
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
{
|
||||
unityConflicts.length > 0 ? (
|
||||
<>
|
||||
<Typography className={"text-red-700"}>
|
||||
There are unity version conflicts
|
||||
</Typography>
|
||||
<List>
|
||||
{unityConflicts.map(([pkgId, _]) => (
|
||||
<ListItem key={pkgId}>
|
||||
{pkgId} does not support your unity version
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
{
|
||||
changes.remove_legacy_files.length > 0 || changes.remove_legacy_folders.length > 0 ? (
|
||||
<>
|
||||
<Typography className={"text-red-700"}>
|
||||
The following legacy files and folders will be removed
|
||||
</Typography>
|
||||
<List>
|
||||
{changes.remove_legacy_files.map(f => (
|
||||
<ListItem key={f}>
|
||||
{f}
|
||||
</ListItem>
|
||||
))}
|
||||
{changes.remove_legacy_folders.map(f => (
|
||||
<ListItem key={f}>
|
||||
{f}
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</>
|
||||
): null
|
||||
}
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
<Button onClick={cancel} className="mr-1">Cancel</Button>
|
||||
<Button onClick={apply} color={"red"}>Apply</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
function RepositoryMenuItem(
|
||||
{
|
||||
hiddenUserRepositories,
|
||||
|
|
@ -266,8 +422,8 @@ interface PackageRowInfo {
|
|||
id: string;
|
||||
infoSource: TauriVersion;
|
||||
displayName: string;
|
||||
unityCompatible: Map<string, TauriBasePackageInfo>;
|
||||
unityIncompatible: Map<string, TauriBasePackageInfo>;
|
||||
unityCompatible: Map<string, TauriPackage>;
|
||||
unityIncompatible: Map<string, TauriPackage>;
|
||||
sources: Set<string>;
|
||||
installed: null | {
|
||||
version: TauriVersion;
|
||||
|
|
@ -394,21 +550,25 @@ function combinePackagesAndProjectDetails(
|
|||
return asArray;
|
||||
}
|
||||
|
||||
type PackageInfo = {
|
||||
installed: string | null;
|
||||
versions: string[];
|
||||
displayName: string;
|
||||
id: string;
|
||||
source: string;
|
||||
};
|
||||
|
||||
function PackageRow({pkg}: { pkg: PackageRowInfo }) {
|
||||
function PackageRow(
|
||||
{
|
||||
pkg,
|
||||
locked,
|
||||
onInstallRequested,
|
||||
onRemoveRequested,
|
||||
}: {
|
||||
pkg: PackageRowInfo;
|
||||
locked: boolean;
|
||||
onInstallRequested: (pkg: TauriPackage) => void;
|
||||
onRemoveRequested: (pkgId: string) => void;
|
||||
}) {
|
||||
const cellClass = "p-2.5";
|
||||
const noGrowCellClass = `${cellClass} w-1`;
|
||||
const versionNames = [...pkg.unityCompatible.keys()];
|
||||
const latestVersion = versionNames[0];
|
||||
const latestVersion: string | undefined = versionNames[0];
|
||||
|
||||
let installedInfo;
|
||||
const notInstalled = "Not Installed";
|
||||
let installedInfo: string;
|
||||
if (pkg.installed) {
|
||||
const version = toVersionString(pkg.installed.version);
|
||||
if (pkg.installed.yanked) {
|
||||
|
|
@ -417,9 +577,27 @@ function PackageRow({pkg}: { pkg: PackageRowInfo }) {
|
|||
installedInfo = version;
|
||||
}
|
||||
} else {
|
||||
installedInfo = "Not Installed";
|
||||
installedInfo = notInstalled;
|
||||
}
|
||||
|
||||
const onChange = (version: string | undefined) => {
|
||||
if (!version) return;
|
||||
const pkgVersion = pkg.unityCompatible.get(version);
|
||||
if (!pkgVersion) return;
|
||||
onInstallRequested(pkgVersion);
|
||||
}
|
||||
|
||||
const installLatest = () => {
|
||||
if (!latestVersion) return;
|
||||
const latest = pkg.unityCompatible.get(latestVersion);
|
||||
if (!latest) return;
|
||||
onInstallRequested(latest);
|
||||
}
|
||||
|
||||
const remove = () => {
|
||||
onRemoveRequested(pkg.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<tr className="even:bg-blue-gray-50/50">
|
||||
<td className={`${cellClass} overflow-hidden max-w-80 overflow-ellipsis`}>
|
||||
|
|
@ -440,8 +618,12 @@ function PackageRow({pkg}: { pkg: PackageRowInfo }) {
|
|||
labelProps={{className: "hidden"}}
|
||||
menuProps={{className: "z-20"}}
|
||||
className={`border-blue-gray-200 ${pkg.installed?.yanked ? "text-red-700" : ""}`}
|
||||
onChange={onChange}
|
||||
selected={() => <>{installedInfo}</>}
|
||||
disabled={locked}
|
||||
>
|
||||
{versionNames.map(v => <Option key={v} value={v}>{v}</Option>)}
|
||||
<Option value={notInstalled} hidden>{notInstalled}</Option>
|
||||
</Select>
|
||||
</td>
|
||||
<td className={noGrowCellClass}>
|
||||
|
|
@ -474,12 +656,13 @@ function PackageRow({pkg}: { pkg: PackageRowInfo }) {
|
|||
{
|
||||
pkg.installed ? (
|
||||
<Tooltip content={"Remove Package"}>
|
||||
<IconButton variant={'text'}><MinusCircleIcon
|
||||
<IconButton variant={'text'} disabled={locked} onClick={remove}><MinusCircleIcon
|
||||
className={"size-5 text-red-700"}/></IconButton>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip content={"Add Package"}>
|
||||
<IconButton variant={'text'}><PlusCircleIcon
|
||||
<IconButton variant={'text'} disabled={locked && !!latestVersion}
|
||||
onClick={installLatest}><PlusCircleIcon
|
||||
className={"size-5 text-gray-800"}/></IconButton>
|
||||
</Tooltip>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ export function environmentPackages() {
|
|||
return invoke()<TauriPackage[]>("environment_packages")
|
||||
}
|
||||
|
||||
export function projectDetails(projectPath: string) {
|
||||
return invoke()<TauriProjectDetails>("project_details", { projectPath })
|
||||
}
|
||||
|
||||
export function environmentRepositoriesInfo() {
|
||||
return invoke()<TauriRepositoriesInfo>("environment_repositories_info")
|
||||
}
|
||||
|
|
@ -38,12 +34,28 @@ export function environmentSetHideLocalUserPackages(value: boolean) {
|
|||
return invoke()<null>("environment_set_hide_local_user_packages", { value })
|
||||
}
|
||||
|
||||
export type TauriVersion = { major: number; minor: number; patch: number; pre: string; build: string }
|
||||
export type TauriProject = { list_version: number; index: number; name: string; path: string; project_type: TauriProjectType; unity: string; last_modified: number; created_at: number }
|
||||
export type TauriRepositoriesInfo = { user_repositories: TauriUserRepository[]; hidden_user_repositories: string[]; hide_local_user_packages: boolean }
|
||||
export type TauriPackage = ({ name: string; display_name: string | null; version: TauriVersion; unity: [number, number] | null; is_yanked: boolean }) & { env_version: number; index: number; source: TauriPackageSource }
|
||||
export function projectDetails(projectPath: string) {
|
||||
return invoke()<TauriProjectDetails>("project_details", { projectPath })
|
||||
}
|
||||
|
||||
export function projectInstallPackage(projectPath: string, envVersion: number, packageIndex: number) {
|
||||
return invoke()<TauriPendingProjectChanges>("project_install_package", { projectPath,envVersion,packageIndex })
|
||||
}
|
||||
|
||||
export function projectApplyPendingChanges(projectPath: string, changesVersion: number) {
|
||||
return invoke()<null>("project_apply_pending_changes", { projectPath,changesVersion })
|
||||
}
|
||||
|
||||
export type TauriProjectType = "Unknown" | "LegacySdk2" | "LegacyWorlds" | "LegacyAvatars" | "UpmWorlds" | "UpmAvatars" | "UpmStarter" | "Worlds" | "Avatars" | "VpmStarter"
|
||||
export type TauriPackageSource = "LocalUser" | { Remote: { id: string; display_name: string } }
|
||||
export type TauriBasePackageInfo = { name: string; display_name: string | null; version: TauriVersion; unity: [number, number] | null; is_yanked: boolean }
|
||||
export type TauriPackage = ({ name: string; display_name: string | null; version: TauriVersion; unity: [number, number] | null; is_yanked: boolean }) & { env_version: number; index: number; source: TauriPackageSource }
|
||||
export type TauriProjectDetails = { unity: [number, number] | null; unity_str: string; installed_packages: ([string, TauriBasePackageInfo])[] }
|
||||
export type TauriPendingProjectChanges = { changes_version: number; package_changes: ([string, TauriPackageChange])[]; remove_legacy_files: string[]; remove_legacy_folders: string[]; conflicts: ([string, TauriConflictInfo])[] }
|
||||
export type TauriRemoveReason = "Requested" | "Legacy" | "Unused"
|
||||
export type TauriRepositoriesInfo = { user_repositories: TauriUserRepository[]; hidden_user_repositories: string[]; hide_local_user_packages: boolean }
|
||||
export type TauriBasePackageInfo = { name: string; display_name: string | null; version: TauriVersion; unity: [number, number] | null; is_yanked: boolean }
|
||||
export type TauriPackageChange = { InstallNew: TauriBasePackageInfo } | { Remove: TauriRemoveReason }
|
||||
export type TauriConflictInfo = { packages: string[]; unity_conflict: boolean }
|
||||
export type TauriPackageSource = "LocalUser" | { Remote: { id: string; display_name: string } }
|
||||
export type TauriUserRepository = { id: string; display_name: string }
|
||||
export type TauriProject = { list_version: number; index: number; name: string; path: string; project_type: TauriProjectType; unity: string; last_modified: number; created_at: number }
|
||||
export type TauriVersion = { major: number; minor: number; patch: number; pre: string; build: string }
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ impl<IO: ProjectIo> UnityProject<IO> {
|
|||
self.manifest.dependencies().map(|(name, _)| name)
|
||||
}
|
||||
|
||||
pub(crate) fn get_locked(&self, name: &str) -> Option<LockedDependencyInfo> {
|
||||
pub fn get_locked(&self, name: &str) -> Option<LockedDependencyInfo> {
|
||||
self.manifest.get_locked(name)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ pub struct PendingProjectChanges<'env> {
|
|||
pub(crate) conflicts: HashMap<Box<str>, ConflictInfo>,
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug)]
|
||||
pub enum PackageChange<'env> {
|
||||
Install(Install<'env>),
|
||||
|
|
@ -88,7 +87,6 @@ impl<'env> Remove<'env> {
|
|||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum RemoveReason {
|
||||
Requested,
|
||||
|
|
|
|||
|
|
@ -174,7 +174,6 @@ fn print_prompt_install(changes: &PendingProjectChanges) {
|
|||
PackageChange::Remove(change) => {
|
||||
removed.push((change.reason(), name));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +214,6 @@ fn print_prompt_install(changes: &PendingProjectChanges) {
|
|||
RemoveReason::Requested => "requested",
|
||||
RemoveReason::Legacy => "legacy",
|
||||
RemoveReason::Unused => "unused",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
println!("- {} (removed since {})", name, reason_name);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue