From 18554785ae919d6a3d613794cc9f04d76a71f8fd Mon Sep 17 00:00:00 2001 From: emirsassan Date: Thu, 10 Jul 2025 11:27:30 +0300 Subject: [PATCH] feat: add screenshots and worlds page --- .../src/components/content/WorldIcon.tsx | 37 ++++++++++++++ .../src/routes/app/cluster/screenshots.tsx | 49 ++++++++++++++----- .../src/routes/app/cluster/worlds.tsx | 45 ++++++++++++++++- packages/core/src/api/cluster/content.rs | 39 +++++++++++++++ packages/core/src/api/cluster/mod.rs | 1 + packages/core/src/api/tauri/commands.rs | 19 +++++++ 6 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 apps/onelauncher/frontend/src/components/content/WorldIcon.tsx create mode 100644 packages/core/src/api/cluster/content.rs diff --git a/apps/onelauncher/frontend/src/components/content/WorldIcon.tsx b/apps/onelauncher/frontend/src/components/content/WorldIcon.tsx new file mode 100644 index 00000000..19874292 --- /dev/null +++ b/apps/onelauncher/frontend/src/components/content/WorldIcon.tsx @@ -0,0 +1,37 @@ +import DefaultWorld from '@/assets/images/default_world.png'; +import { convertFileSrc } from '@tauri-apps/api/core'; +import { dataDir, join } from '@tauri-apps/api/path'; +import { useEffect, useState } from 'react'; +import { twMerge } from 'tailwind-merge'; + +export interface WorldIconProps { + world_name: string; + cluster_name: string; + className?: string; +} + +export default function WorldIcon(props: WorldIconProps) { + const { world_name, cluster_name, className } = props; + const [imagePath, setImagePath] = useState(''); + + useEffect(() => { + async function loadImagePath() { + const iconPath = await join(await dataDir(), 'OneLauncher', 'clusters', cluster_name, 'saves', world_name, 'icon.png'); + setImagePath(convertFileSrc(iconPath)); + } + + loadImagePath().catch(console.error); + }, [cluster_name, world_name]); + + return ( + {`${world_name} { + e.currentTarget.src = DefaultWorld; + }} + src={imagePath} + /> + ); +} diff --git a/apps/onelauncher/frontend/src/routes/app/cluster/screenshots.tsx b/apps/onelauncher/frontend/src/routes/app/cluster/screenshots.tsx index 9353c645..78f3be17 100644 --- a/apps/onelauncher/frontend/src/routes/app/cluster/screenshots.tsx +++ b/apps/onelauncher/frontend/src/routes/app/cluster/screenshots.tsx @@ -1,6 +1,11 @@ import ScrollableContainer from '@/components/ScrollableContainer'; +import { bindings } from '@/main'; +import { useCommand } from '@onelauncher/common'; import { Show } from '@onelauncher/common/components'; import { createFileRoute } from '@tanstack/react-router'; +import { convertFileSrc } from '@tauri-apps/api/core'; +import { dataDir, join } from '@tauri-apps/api/path'; +import { useEffect, useState } from 'react'; import Sidebar from '../settings/route'; export const Route = createFileRoute('/app/cluster/screenshots')({ @@ -8,7 +13,10 @@ export const Route = createFileRoute('/app/cluster/screenshots')({ }); function RouteComponent() { - const arr = [1, 2, 3, 4, 5, 6]; + const { id } = Route.useSearch(); + const cluster = useCommand('getClusterById', () => bindings.core.getClusterById(Number(id.toString()) as unknown as bigint)); + const result = useCommand('getScreenshots', () => bindings.core.getScreenshots(Number(id.toString()) as unknown as bigint)); + return (

Screenshots

@@ -16,11 +24,12 @@ function RouteComponent() {
No screenshots found. Press F2 in game to take a screenshot!
} - when + // @ts-ignore idk + when={result.data?.length > 0} >
- {arr.map(data => ( - + {result.data?.map(data => ( + ))}
@@ -32,20 +41,34 @@ function RouteComponent() { function ScreenshotEntry({ path, + cluster_path, }: { path: string; cluster_path: string; }) { - function onClick() { - // screen shot viewer overlay - } + const [imagePath, setImagePath] = useState(''); + + useEffect(() => { + async function loadImagePath() { + const screenshotsDir = await join(await dataDir(), 'OneLauncher', 'clusters', cluster_path, 'screenshots'); + const fullPath = await join(screenshotsDir, path); + setImagePath(convertFileSrc(fullPath)); + } + + loadImagePath().catch(console.error); + }, [cluster_path, path]); return ( -
- {path} -
+ <> +
+ {path} +
+ ); } diff --git a/apps/onelauncher/frontend/src/routes/app/cluster/worlds.tsx b/apps/onelauncher/frontend/src/routes/app/cluster/worlds.tsx index c1ea46be..00040da9 100644 --- a/apps/onelauncher/frontend/src/routes/app/cluster/worlds.tsx +++ b/apps/onelauncher/frontend/src/routes/app/cluster/worlds.tsx @@ -1,5 +1,10 @@ +import WorldIcon from '@/components/content/WorldIcon'; import ScrollableContainer from '@/components/ScrollableContainer'; +import { bindings } from '@/main'; +import { useCommand } from '@onelauncher/common'; +import { Button, Show } from '@onelauncher/common/components'; import { createFileRoute } from '@tanstack/react-router'; +import { Trash01Icon } from '@untitled-theme/icons-react'; import Sidebar from '../settings/route'; export const Route = createFileRoute('/app/cluster/worlds')({ @@ -7,14 +12,52 @@ export const Route = createFileRoute('/app/cluster/worlds')({ }); function RouteComponent() { + const { id } = Route.useSearch(); + const cluster = useCommand('getClusterById', () => bindings.core.getClusterById(Number(id.toString()) as unknown as bigint)); + const list = useCommand('getWorlds', () => bindings.core.getWorlds(Number(id.toString()) as unknown as bigint)); + return (

Worlds

- + No worlds found.
} + when={list.data !== undefined && list.data.length > 0} + > +
+ {list.data?.map(data => ( + + ))} +
+
); } + +function WorldEntry(props: { name: string; cluster_path: string }) { + const { name, cluster_path } = props; + + return ( +
+
+ +
+

{name}

+

Todo

+
+
+ +
+
+
+ ); +} diff --git a/packages/core/src/api/cluster/content.rs b/packages/core/src/api/cluster/content.rs new file mode 100644 index 00000000..d7bacaf5 --- /dev/null +++ b/packages/core/src/api/cluster/content.rs @@ -0,0 +1,39 @@ +use onelauncher_entity::clusters; + +use crate::{error::LauncherResult, store::Dirs, utils::io}; + +pub async fn get_screenshots(cluster: &clusters::Model) -> LauncherResult> { + let dir = cluster.folder_name.clone(); + let path = Dirs::get_clusters_dir().await?.join(dir).join("screenshots"); + + if !path.exists() { + io::create_dir(&path).await?; + return Ok(Vec::new()); + } + + let mut list = vec![]; + let mut files = io::read_dir(path).await?; + while let Ok(Some(entry)) = files.next_entry().await { + list.push(entry.file_name().to_string_lossy().to_string()); + } + + Ok(list) +} + +pub async fn get_worlds(cluster: &clusters::Model) -> LauncherResult> { + let dir = cluster.folder_name.clone(); + let path = Dirs::get_clusters_dir().await?.join(dir).join("saves"); + + if !path.exists() { + io::create_dir(&path).await?; + return Ok(Vec::new()); + } + + let mut list = vec![]; + let mut files = io::read_dir(path).await?; + while let Ok(Some(entry)) = files.next_entry().await { + list.push(entry.file_name().to_string_lossy().to_string()); + } + + Ok(list) +} diff --git a/packages/core/src/api/cluster/mod.rs b/packages/core/src/api/cluster/mod.rs index f565e69f..09b031d4 100644 --- a/packages/core/src/api/cluster/mod.rs +++ b/packages/core/src/api/cluster/mod.rs @@ -18,6 +18,7 @@ use crate::store::{Dirs, State}; use crate::utils::io::{self, IOError}; pub mod dao; +pub mod content; mod sync; pub use sync::*; diff --git a/packages/core/src/api/tauri/commands.rs b/packages/core/src/api/tauri/commands.rs index 11410863..6ceb336b 100644 --- a/packages/core/src/api/tauri/commands.rs +++ b/packages/core/src/api/tauri/commands.rs @@ -31,6 +31,12 @@ pub trait TauriLauncherApi { #[taurpc(alias = "updateClusterById")] async fn update_cluster_by_id(id: ClusterId, request: ClusterUpdate) -> LauncherResult<()>; + #[taurpc(alias = "getScreenshots")] + async fn get_screenshots(id: ClusterId) -> LauncherResult>; + + #[taurpc(alias = "getWorlds")] + async fn get_worlds(id: ClusterId) -> LauncherResult>; + // Setting Profiles #[taurpc(alias = "getProfileOrDefault")] @@ -184,6 +190,19 @@ impl TauriLauncherApi for TauriLauncherApiImpl { Ok(()) } + async fn get_screenshots(self, id: ClusterId) -> LauncherResult> { + let cluster = api::cluster::dao::get_cluster_by_id(id).await? + .ok_or_else(|| anyhow::anyhow!("cluster with id {} not found", id))?; + + api::cluster::content::get_screenshots(&cluster).await + } + + async fn get_worlds(self, id: ClusterId) -> LauncherResult> { + let cluster = api::cluster::dao::get_cluster_by_id(id).await? + .ok_or_else(|| anyhow::anyhow!("cluster with id {} not found", id))?; + + api::cluster::content::get_worlds(&cluster).await + } // Setting Profiles async fn get_global_profile(self) -> SettingsProfile {