feat: add screenshots and worlds page
This commit is contained in:
parent
72157f689b
commit
18554785ae
6 changed files with 176 additions and 14 deletions
|
|
@ -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<string>('');
|
||||
|
||||
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 (
|
||||
<img
|
||||
{...props}
|
||||
alt={`${world_name} icon`}
|
||||
className={twMerge('rounded-md size-16', className)}
|
||||
onError={(e) => {
|
||||
e.currentTarget.src = DefaultWorld;
|
||||
}}
|
||||
src={imagePath}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<Sidebar.Page>
|
||||
<h1>Screenshots</h1>
|
||||
|
|
@ -16,11 +24,12 @@ function RouteComponent() {
|
|||
<div className="h-full">
|
||||
<Show
|
||||
fallback={<div className="text-border/400">No screenshots found. Press F2 in game to take a screenshot!</div>}
|
||||
when
|
||||
// @ts-ignore idk
|
||||
when={result.data?.length > 0}
|
||||
>
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(350px,1fr))] w-full transform-gpu gap-2">
|
||||
{arr.map(data => (
|
||||
<ScreenshotEntry cluster_path="cluster" key={data} path={`screenshot-${data}.png`} />
|
||||
{result.data?.map(data => (
|
||||
<ScreenshotEntry cluster_path={cluster.data?.folder_name || ''} key={data} path={data} />
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
|
|
@ -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<string>('');
|
||||
|
||||
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 (
|
||||
<div
|
||||
className="flex flex-col items-center gap-3 rounded-xl bg-component-bg p-3 active:bg-component-bg-pressed hover:bg-component-bg-hover hover:opacity-80"
|
||||
onClick={onClick}
|
||||
>
|
||||
<img alt={path} className="aspect-video w-full rounded-lg" src="https://github.com/emirsassan.png" />
|
||||
</div>
|
||||
<>
|
||||
<div
|
||||
className="flex flex-col items-center gap-3 rounded-xl bg-component-bg p-3 active:bg-component-bg-pressed hover:bg-component-bg-hover hover:opacity-80 cursor-pointer"
|
||||
>
|
||||
<img
|
||||
alt={path}
|
||||
className="aspect-video w-full rounded-lg"
|
||||
src={imagePath}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<Sidebar.Page>
|
||||
<h1>Worlds</h1>
|
||||
<ScrollableContainer>
|
||||
<div className="h-full">
|
||||
|
||||
<Show
|
||||
fallback={<div className="text-border/400">No worlds found.</div>}
|
||||
when={list.data !== undefined && list.data.length > 0}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
{list.data?.map(data => (
|
||||
<WorldEntry cluster_path={cluster.data?.folder_name || ''} key={data} name={data} />
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
</Sidebar.Page>
|
||||
);
|
||||
}
|
||||
|
||||
function WorldEntry(props: { name: string; cluster_path: string }) {
|
||||
const { name, cluster_path } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-row items-center justify-between gap-3 rounded-xl bg-component-bg p-3 active:bg-component-bg-pressed hover:bg-component-bg-hover"
|
||||
>
|
||||
<div className="flex flex-row items-center gap-x-3">
|
||||
<WorldIcon className="aspect-ratio-square h-16 w-16" cluster_name={cluster_path} world_name={name} />
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<h3>{name}</h3>
|
||||
<p>Todo</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row items-center justify-end gap-x-3">
|
||||
<Button
|
||||
children={<Trash01Icon />}
|
||||
color="danger"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
39
packages/core/src/api/cluster/content.rs
Normal file
39
packages/core/src/api/cluster/content.rs
Normal file
|
|
@ -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<Vec<String>> {
|
||||
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<Vec<String>> {
|
||||
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)
|
||||
}
|
||||
|
|
@ -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::*;
|
||||
|
|
|
|||
|
|
@ -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<Vec<String>>;
|
||||
|
||||
#[taurpc(alias = "getWorlds")]
|
||||
async fn get_worlds(id: ClusterId) -> LauncherResult<Vec<String>>;
|
||||
|
||||
|
||||
// Setting Profiles
|
||||
#[taurpc(alias = "getProfileOrDefault")]
|
||||
|
|
@ -184,6 +190,19 @@ impl TauriLauncherApi for TauriLauncherApiImpl {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_screenshots(self, id: ClusterId) -> LauncherResult<Vec<String>> {
|
||||
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<Vec<String>> {
|
||||
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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue