From c18cef2f71f475fe5fddece40eb527292aa121d4 Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Sat, 10 May 2025 23:44:34 +0100 Subject: [PATCH 01/13] feat: screenshots backend stuff --- apps/app-frontend/src/helpers/screenshots.ts | 10 +++ .../app-frontend/src/pages/instance/Index.vue | 4 ++ .../src/pages/instance/Screenshots.vue | 23 +++++++ apps/app-frontend/src/pages/instance/index.js | 3 +- apps/app-frontend/src/routes.js | 9 +++ apps/app/build.rs | 8 +++ apps/app/capabilities/plugins.json | 3 +- apps/app/src/api/mod.rs | 1 + apps/app/src/api/screenshots.rs | 18 ++++++ apps/app/src/main.rs | 1 + packages/app-lib/src/api/mod.rs | 1 + packages/app-lib/src/api/screenshots.rs | 61 +++++++++++++++++++ 12 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 apps/app-frontend/src/helpers/screenshots.ts create mode 100644 apps/app-frontend/src/pages/instance/Screenshots.vue create mode 100644 apps/app/src/api/screenshots.rs create mode 100644 packages/app-lib/src/api/screenshots.rs diff --git a/apps/app-frontend/src/helpers/screenshots.ts b/apps/app-frontend/src/helpers/screenshots.ts new file mode 100644 index 0000000000..5cd2e694f3 --- /dev/null +++ b/apps/app-frontend/src/helpers/screenshots.ts @@ -0,0 +1,10 @@ +import {invoke} from "@tauri-apps/api/core"; + +type Screenshot = { + filename: string; + creation_date: string; +} + +export async function getAllProfileScreenshots(path: string): Promise { + return await invoke('plugin:screenshots|get_all_profile_screenshots', { path }) +} \ No newline at end of file diff --git a/apps/app-frontend/src/pages/instance/Index.vue b/apps/app-frontend/src/pages/instance/Index.vue index 65bfbf68f3..9231784c7d 100644 --- a/apps/app-frontend/src/pages/instance/Index.vue +++ b/apps/app-frontend/src/pages/instance/Index.vue @@ -272,6 +272,10 @@ const tabs = computed(() => [ label: 'Worlds', href: `${basePath.value}/worlds`, }, + { + label: 'Screenshots', + href: `${basePath.value}/screenshots` + }, { label: 'Logs', href: `${basePath.value}/logs`, diff --git a/apps/app-frontend/src/pages/instance/Screenshots.vue b/apps/app-frontend/src/pages/instance/Screenshots.vue new file mode 100644 index 0000000000..3463006b24 --- /dev/null +++ b/apps/app-frontend/src/pages/instance/Screenshots.vue @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/apps/app-frontend/src/pages/instance/index.js b/apps/app-frontend/src/pages/instance/index.js index fa77df524e..027f6f11c0 100644 --- a/apps/app-frontend/src/pages/instance/index.js +++ b/apps/app-frontend/src/pages/instance/index.js @@ -3,5 +3,6 @@ import Overview from './Overview.vue' import Worlds from './Worlds.vue' import Mods from './Mods.vue' import Logs from './Logs.vue' +import Screenshots from "./Screenshots.vue" -export { Index, Overview, Worlds, Mods, Logs } +export { Index, Overview, Worlds, Mods, Logs, Screenshots } diff --git a/apps/app-frontend/src/routes.js b/apps/app-frontend/src/routes.js index 6d5e4e3726..c7bbfb58cd 100644 --- a/apps/app-frontend/src/routes.js +++ b/apps/app-frontend/src/routes.js @@ -132,6 +132,15 @@ export default new createRouter({ breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Worlds' }], }, }, + { + path: 'screenshots', + name: 'Screenshots', + component: Instance.Screenshots, + meta: { + useRootContext: true, + breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Screenshots' }], + }, + }, { path: '', name: 'Mods', diff --git a/apps/app/build.rs b/apps/app/build.rs index 644d22b681..f162f853f1 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -264,6 +264,14 @@ fn main() { .default_permission( DefaultPermissionRule::AllowAllCommands, ), + ) + .plugin( + "screenshots", + InlinedPlugin::new() + .commands(&["get_all_profile_screenshots"]) + .default_permission( + DefaultPermissionRule::AllowAllCommands, + ), ), ) .expect("Failed to run tauri-build"); diff --git a/apps/app/capabilities/plugins.json b/apps/app/capabilities/plugins.json index b9777b6d9f..7a196d09e0 100644 --- a/apps/app/capabilities/plugins.json +++ b/apps/app/capabilities/plugins.json @@ -36,6 +36,7 @@ "utils:default", "ads:default", "friends:default", - "worlds:default" + "worlds:default", + "screenshots:default" ] } diff --git a/apps/app/src/api/mod.rs b/apps/app/src/api/mod.rs index 09d37e87ab..1338f7c6a5 100644 --- a/apps/app/src/api/mod.rs +++ b/apps/app/src/api/mod.rs @@ -20,6 +20,7 @@ pub mod ads; pub mod cache; pub mod friends; pub mod worlds; +pub mod screenshots; pub type Result = std::result::Result; diff --git a/apps/app/src/api/screenshots.rs b/apps/app/src/api/screenshots.rs new file mode 100644 index 0000000000..cbe2c834a9 --- /dev/null +++ b/apps/app/src/api/screenshots.rs @@ -0,0 +1,18 @@ +use tauri::{AppHandle, Runtime}; +use theseus::{screenshots}; + +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("screenshots") + .invoke_handler(tauri::generate_handler![ + get_all_profile_screenshots + ]) + .build() +} + +#[tauri::command] +pub async fn get_all_profile_screenshots( + app_handle: AppHandle, + path: &str, +) -> crate::api::Result> { + Ok(screenshots::get_all_profile_screenshots(path).await?) +} \ No newline at end of file diff --git a/apps/app/src/main.rs b/apps/app/src/main.rs index 4291431df8..e19b3e239a 100644 --- a/apps/app/src/main.rs +++ b/apps/app/src/main.rs @@ -260,6 +260,7 @@ fn main() { .plugin(api::ads::init()) .plugin(api::friends::init()) .plugin(api::worlds::init()) + .plugin(api::screenshots::init()) .invoke_handler(tauri::generate_handler![ initialize_state, is_dev, diff --git a/packages/app-lib/src/api/mod.rs b/packages/app-lib/src/api/mod.rs index 421d805c1f..2a44b79b74 100644 --- a/packages/app-lib/src/api/mod.rs +++ b/packages/app-lib/src/api/mod.rs @@ -13,6 +13,7 @@ pub mod profile; pub mod settings; pub mod tags; pub mod worlds; +pub mod screenshots; pub mod data { pub use crate::state::{ diff --git a/packages/app-lib/src/api/screenshots.rs b/packages/app-lib/src/api/screenshots.rs new file mode 100644 index 0000000000..1bbe83725b --- /dev/null +++ b/packages/app-lib/src/api/screenshots.rs @@ -0,0 +1,61 @@ +use std::ffi::OsStr; +use std::path::Path; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use tokio::fs::canonicalize; +use crate::profile::get_full_path; +use crate::util::io::{metadata, read_dir}; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct Screenshot { + pub path: String, + pub creation_date: DateTime +} + +pub async fn get_all_profile_screenshots( + profile_path: &str, +) -> crate::Result> { + get_all_screenshots_in_profile(&get_full_path(profile_path).await?) + .await +} + +async fn get_all_screenshots_in_profile( + profile_dir: &Path, +) -> crate::Result> { + let screenshots_dir = profile_dir.join("screenshots"); + if metadata(&screenshots_dir).await.is_err() { + return Ok(Vec::new()); + } + + let mut dir = read_dir(&screenshots_dir).await?; + let mut screenshots = Vec::new(); + + while let Some(entry) = dir.next_entry().await? { + if !entry.file_type().await?.is_file() { + continue; + } + + let path = entry.path(); + if path + .extension() + .and_then(OsStr::to_str) + .map(|ext| ext.eq_ignore_ascii_case("png")) + != Some(true) + { + continue; + } + + let abs_path: std::path::PathBuf = canonicalize(&path).await?; + let full_path = abs_path.to_string_lossy().into_owned(); + + let meta = entry.metadata().await?; + let created_time = meta.created().unwrap_or(meta.modified()?); + let creation_date = DateTime::::from(created_time); + + screenshots.push(Screenshot { path: full_path, creation_date }); + } + + screenshots.sort_by_key(|s| s.creation_date); + + Ok(screenshots) +} \ No newline at end of file From 18c1464a12d67cba46eeb36390e173550a637bd6 Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Sun, 11 May 2025 01:12:15 +0100 Subject: [PATCH 02/13] feat: Improve layout to make it closer to the draft design. --- .../src/components/ui/ScreenshotCard.vue | 89 +++++++++++++++ apps/app-frontend/src/helpers/screenshots.ts | 13 ++- .../src/pages/instance/Screenshots.vue | 102 ++++++++++++++++-- apps/app/src/api/mod.rs | 2 +- apps/app/src/api/screenshots.rs | 13 +-- packages/app-lib/src/api/mod.rs | 10 +- packages/app-lib/src/api/screenshots.rs | 30 ++++-- 7 files changed, 221 insertions(+), 38 deletions(-) create mode 100644 apps/app-frontend/src/components/ui/ScreenshotCard.vue diff --git a/apps/app-frontend/src/components/ui/ScreenshotCard.vue b/apps/app-frontend/src/components/ui/ScreenshotCard.vue new file mode 100644 index 0000000000..b3628d3d4b --- /dev/null +++ b/apps/app-frontend/src/components/ui/ScreenshotCard.vue @@ -0,0 +1,89 @@ + + + \ No newline at end of file diff --git a/apps/app-frontend/src/helpers/screenshots.ts b/apps/app-frontend/src/helpers/screenshots.ts index 5cd2e694f3..1125265348 100644 --- a/apps/app-frontend/src/helpers/screenshots.ts +++ b/apps/app-frontend/src/helpers/screenshots.ts @@ -1,10 +1,19 @@ import {invoke} from "@tauri-apps/api/core"; -type Screenshot = { - filename: string; +export type Screenshot = { + path: string; creation_date: string; + data: string; } export async function getAllProfileScreenshots(path: string): Promise { return await invoke('plugin:screenshots|get_all_profile_screenshots', { path }) +} + +export async function deleteScreenshotFile(screenshot: Screenshot): Promise { + return await invoke('plugin:screenshots|delete_screenshot', {path: screenshot.path}) +} + +export async function renameScreenshotFile(screenshot: Screenshot, new_filename: string): Promise { + return await invoke('plugin:screenshots|rename_screenshot', {path: screenshot.path, new_filename}) } \ No newline at end of file diff --git a/apps/app-frontend/src/pages/instance/Screenshots.vue b/apps/app-frontend/src/pages/instance/Screenshots.vue index 3463006b24..7998dae3b9 100644 --- a/apps/app-frontend/src/pages/instance/Screenshots.vue +++ b/apps/app-frontend/src/pages/instance/Screenshots.vue @@ -1,23 +1,103 @@ \ No newline at end of file diff --git a/apps/app/src/api/mod.rs b/apps/app/src/api/mod.rs index 1338f7c6a5..c2d08c210e 100644 --- a/apps/app/src/api/mod.rs +++ b/apps/app/src/api/mod.rs @@ -19,8 +19,8 @@ pub mod utils; pub mod ads; pub mod cache; pub mod friends; -pub mod worlds; pub mod screenshots; +pub mod worlds; pub type Result = std::result::Result; diff --git a/apps/app/src/api/screenshots.rs b/apps/app/src/api/screenshots.rs index cbe2c834a9..da80284991 100644 --- a/apps/app/src/api/screenshots.rs +++ b/apps/app/src/api/screenshots.rs @@ -1,18 +1,15 @@ -use tauri::{AppHandle, Runtime}; -use theseus::{screenshots}; +use tauri::Runtime; +use theseus::screenshots; pub fn init() -> tauri::plugin::TauriPlugin { tauri::plugin::Builder::new("screenshots") - .invoke_handler(tauri::generate_handler![ - get_all_profile_screenshots - ]) + .invoke_handler(tauri::generate_handler![get_all_profile_screenshots]) .build() } #[tauri::command] -pub async fn get_all_profile_screenshots( - app_handle: AppHandle, +pub async fn get_all_profile_screenshots( path: &str, ) -> crate::api::Result> { Ok(screenshots::get_all_profile_screenshots(path).await?) -} \ No newline at end of file +} diff --git a/packages/app-lib/src/api/mod.rs b/packages/app-lib/src/api/mod.rs index 2a44b79b74..6a09ed4d6f 100644 --- a/packages/app-lib/src/api/mod.rs +++ b/packages/app-lib/src/api/mod.rs @@ -10,10 +10,10 @@ pub mod mr_auth; pub mod pack; pub mod process; pub mod profile; +pub mod screenshots; pub mod settings; pub mod tags; pub mod worlds; -pub mod screenshots; pub mod data { pub use crate::state::{ @@ -28,12 +28,12 @@ pub mod data { pub mod prelude { pub use crate::{ - State, data::*, event::CommandPayload, - jre, metadata, minecraft_auth, mr_auth, pack, process, - profile::{self, Profile, create}, + jre, + metadata, minecraft_auth, mr_auth, pack, process, profile::{self, create, Profile}, settings, - util::io::{IOError, canonicalize}, + util::io::{canonicalize, IOError}, + State, }; } diff --git a/packages/app-lib/src/api/screenshots.rs b/packages/app-lib/src/api/screenshots.rs index 1bbe83725b..f6dde1aaa0 100644 --- a/packages/app-lib/src/api/screenshots.rs +++ b/packages/app-lib/src/api/screenshots.rs @@ -1,22 +1,23 @@ -use std::ffi::OsStr; -use std::path::Path; -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use tokio::fs::canonicalize; use crate::profile::get_full_path; use crate::util::io::{metadata, read_dir}; +use base64::{engine::general_purpose::STANDARD, Engine}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::ffi::OsStr; +use std::path::Path; +use tokio::fs::{canonicalize, read}; #[derive(Deserialize, Serialize, Debug, Clone)] pub struct Screenshot { pub path: String, - pub creation_date: DateTime + pub creation_date: DateTime, + pub data: String, } pub async fn get_all_profile_screenshots( profile_path: &str, ) -> crate::Result> { - get_all_screenshots_in_profile(&get_full_path(profile_path).await?) - .await + get_all_screenshots_in_profile(&get_full_path(profile_path).await?).await } async fn get_all_screenshots_in_profile( @@ -52,10 +53,17 @@ async fn get_all_screenshots_in_profile( let created_time = meta.created().unwrap_or(meta.modified()?); let creation_date = DateTime::::from(created_time); - screenshots.push(Screenshot { path: full_path, creation_date }); + let bytes = read(&abs_path).await?; + let data = Engine::encode(&STANDARD, &bytes); + + screenshots.push(Screenshot { + path: full_path, + creation_date, + data, + }); } screenshots.sort_by_key(|s| s.creation_date); - + Ok(screenshots) -} \ No newline at end of file +} From a6277e02805b30176290b4b74138a7a5b09f5588 Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Sun, 11 May 2025 01:15:23 +0100 Subject: [PATCH 03/13] fix: lint issues --- .../src/components/ui/ScreenshotCard.vue | 41 +++---- apps/app-frontend/src/helpers/screenshots.ts | 22 ++-- .../app-frontend/src/pages/instance/Index.vue | 32 ++--- .../src/pages/instance/Screenshots.vue | 115 ++++++++++-------- apps/app-frontend/src/pages/instance/index.js | 2 +- 5 files changed, 109 insertions(+), 103 deletions(-) diff --git a/apps/app-frontend/src/components/ui/ScreenshotCard.vue b/apps/app-frontend/src/components/ui/ScreenshotCard.vue index b3628d3d4b..ef2e37015d 100644 --- a/apps/app-frontend/src/components/ui/ScreenshotCard.vue +++ b/apps/app-frontend/src/components/ui/ScreenshotCard.vue @@ -16,11 +16,7 @@ -