mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 03:55:59 +00:00
feat(instance-sync): screenshots sync (#7218)
* pnpm prepr * feat(instance-sync): screenshots sync * fix: prepr + fmt * fix: qa * feat: screenshit editor * feat: screenshot* editor qa * fix: qa * fix: lint * fix: aecsocket rev * qa: a11y tab navigation focus is cut off * qa: Change “Edited” badge to be bg-highlight-green * qa: friends list input style wrong * qa: toolbar width + labels * qa: multiselect changes * qa: anim changes * qa: card hover colors * qa: copy feedback * qa: hide date * qa: instance icon in overflow/contextmenu * qa: spacing * qa: group empty state text * qa: drag preview badge * qa: group deletion skip warning if no screenshots * qa: redesign editor * qa: remove screenshot parent/edited badge stuff * qa: control font size consistency * qa: viewer copy control * qa: editor fixes * qa: redirect on disable screenshot sync * qa: margin police * fix: crop * fix: qa * feat: initial start on basic instance file syncing (#7220) * qa: final * qa: final 2 * fix: lint + prepr * fix: copy * fix: screenshot editing outside of app causing ghost files in db + sync fail rollback impl * chore: split up * fix: fmt --------- Co-authored-by: tdgao <mr.trumgao@gmail.com>
This commit is contained in:
@@ -4,6 +4,9 @@ use path_util::SafeRelativeUtf8UnixPathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tauri::{AppHandle, Manager, Runtime};
|
||||
use tauri_plugin_fs::FsExt;
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
use theseus::DownloadReason;
|
||||
use theseus::data::{
|
||||
AppliedContentSetPatch, ContentItem, Dependency,
|
||||
@@ -43,6 +46,32 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
instance_get_optimal_jre_key,
|
||||
instance_get_full_path,
|
||||
instance_get_mod_full_path,
|
||||
instance_list_screenshots,
|
||||
instance_list_all_screenshots,
|
||||
instance_list_synced_screenshots,
|
||||
instance_save_edited_screenshot,
|
||||
instance_list_screenshot_groups,
|
||||
instance_create_screenshot_group,
|
||||
instance_rename_screenshot_group,
|
||||
instance_delete_screenshot_group,
|
||||
instance_set_screenshot_group_memberships,
|
||||
instance_import_screenshot_groups,
|
||||
instance_delete_screenshots,
|
||||
instance_export_screenshots,
|
||||
instance_move_screenshots,
|
||||
instance_open_screenshot,
|
||||
instance_set_synced_option,
|
||||
instance_get_synced_option_join_preview,
|
||||
instance_get_synced_options_overview,
|
||||
instance_get_global_synced_options,
|
||||
instance_set_global_synced_option,
|
||||
instance_get_command_history,
|
||||
instance_set_command_history,
|
||||
instance_open_synced_options_folder,
|
||||
instance_list_synced_servers,
|
||||
instance_update_synced_server,
|
||||
instance_remove_synced_server,
|
||||
instance_rebuild_synced_options,
|
||||
instance_check_installed,
|
||||
instance_update_all,
|
||||
instance_update_project,
|
||||
@@ -94,6 +123,7 @@ pub struct Instance {
|
||||
pub loader: ModLoader,
|
||||
pub loader_version: Option<String>,
|
||||
pub group_ids: Vec<String>,
|
||||
pub synced_options: InstanceSyncedOptions,
|
||||
pub link: Option<InstanceLink>,
|
||||
pub shared_instance: Option<SharedInstanceAttachment>,
|
||||
pub quarantined: bool,
|
||||
@@ -112,6 +142,19 @@ pub struct Instance {
|
||||
pub hooks: Hooks,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct InstanceScreenshot {
|
||||
pub id: String,
|
||||
pub instance_id: String,
|
||||
pub instance_name: String,
|
||||
pub file_name: String,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
pub modified_at: i64,
|
||||
pub group_id: Option<String>,
|
||||
pub path: PathBuf,
|
||||
pub url: url::Url,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum InstanceLink {
|
||||
@@ -258,6 +301,7 @@ impl From<InstanceMetadata> for Instance {
|
||||
loader: metadata.applied_content_set.loader,
|
||||
loader_version: metadata.applied_content_set.loader_version,
|
||||
group_ids: metadata.group_ids,
|
||||
synced_options: metadata.synced_options,
|
||||
link: InstanceLink::from_core(metadata.link),
|
||||
shared_instance: metadata.shared_instance.map(Into::into),
|
||||
quarantined: metadata.quarantined,
|
||||
@@ -632,6 +676,268 @@ pub async fn instance_get_mod_full_path(
|
||||
Ok(theseus::instance::get_mod_full_path(instance_id, project_path).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_list_screenshots<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
instance_id: &str,
|
||||
) -> Result<Vec<InstanceScreenshot>> {
|
||||
let screenshots = theseus::instance::list_screenshots(instance_id).await?;
|
||||
serialize_screenshots(&app_handle, screenshots)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_list_all_screenshots<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
) -> Result<Vec<InstanceScreenshot>> {
|
||||
let screenshots = theseus::instance::list_all_screenshots().await?;
|
||||
serialize_screenshots(&app_handle, screenshots)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_list_synced_screenshots<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
) -> Result<Vec<InstanceScreenshot>> {
|
||||
let screenshots = theseus::instance::list_synced_screenshots().await?;
|
||||
serialize_screenshots(&app_handle, screenshots)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_save_edited_screenshot<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
key: theseus::instance::ScreenshotKey,
|
||||
png_bytes: Vec<u8>,
|
||||
mode: theseus::instance::ScreenshotEditSaveMode,
|
||||
) -> Result<InstanceScreenshot> {
|
||||
let screenshot =
|
||||
theseus::instance::save_edited_screenshot(key, png_bytes, mode).await?;
|
||||
serialize_screenshot(&app_handle, screenshot)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_list_screenshot_groups()
|
||||
-> Result<Vec<theseus::instance::ScreenshotGroup>> {
|
||||
Ok(theseus::instance::list_screenshot_groups().await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_create_screenshot_group(
|
||||
name: String,
|
||||
screenshot_ids: Vec<String>,
|
||||
) -> Result<theseus::instance::ScreenshotGroup> {
|
||||
Ok(
|
||||
theseus::instance::create_screenshot_group(name, screenshot_ids)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_rename_screenshot_group(
|
||||
id: String,
|
||||
new_name: String,
|
||||
) -> Result<theseus::instance::ScreenshotGroup> {
|
||||
Ok(theseus::instance::rename_screenshot_group(id, new_name).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_delete_screenshot_group(id: String) -> Result<()> {
|
||||
Ok(theseus::instance::delete_screenshot_group(id).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_set_screenshot_group_memberships(
|
||||
updates: Vec<theseus::instance::ScreenshotGroupMembershipUpdate>,
|
||||
) -> Result<()> {
|
||||
Ok(theseus::instance::set_screenshot_group_memberships(updates).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_import_screenshot_groups(
|
||||
groups: Vec<theseus::instance::ScreenshotGroupImport>,
|
||||
) -> Result<()> {
|
||||
Ok(theseus::instance::import_screenshot_groups(groups).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_delete_screenshots(
|
||||
keys: Vec<theseus::instance::ScreenshotKey>,
|
||||
) -> Result<()> {
|
||||
Ok(theseus::instance::delete_screenshots(&keys).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_export_screenshots(
|
||||
keys: Vec<theseus::instance::ScreenshotKey>,
|
||||
export_path: PathBuf,
|
||||
) -> Result<()> {
|
||||
Ok(theseus::instance::export_screenshots(&keys, export_path).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_move_screenshots(
|
||||
keys: Vec<theseus::instance::ScreenshotKey>,
|
||||
target_instance_id: &str,
|
||||
) -> Result<Vec<theseus::instance::ScreenshotKey>> {
|
||||
Ok(theseus::instance::move_screenshots(&keys, target_instance_id).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_open_screenshot<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
key: theseus::instance::ScreenshotKey,
|
||||
) -> Result<()> {
|
||||
let path = theseus::instance::get_screenshot_path(&key).await?;
|
||||
app_handle
|
||||
.opener()
|
||||
.reveal_item_in_dir(path)
|
||||
.map_err(|error| std::io::Error::other(error.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_set_synced_option(
|
||||
instance_id: &str,
|
||||
option: InstanceSyncedOption,
|
||||
enabled: bool,
|
||||
resolution: Option<theseus::instance::SyncedOptionJoinResolution>,
|
||||
) -> Result<Instance> {
|
||||
Ok(Instance::from(
|
||||
theseus::instance::set_synced_option(
|
||||
instance_id,
|
||||
option,
|
||||
enabled,
|
||||
resolution,
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_get_synced_option_join_preview(
|
||||
instance_id: &str,
|
||||
option: InstanceSyncedOption,
|
||||
) -> Result<theseus::instance::SyncedOptionJoinPreview> {
|
||||
Ok(
|
||||
theseus::instance::get_synced_option_join_preview(instance_id, option)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_get_synced_options_overview(
|
||||
instance_id: &str,
|
||||
) -> Result<theseus::instance::SyncedOptionsOverview> {
|
||||
Ok(theseus::instance::get_synced_options_overview(instance_id).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_get_global_synced_options()
|
||||
-> Result<theseus::instance::GlobalSyncedOptions> {
|
||||
Ok(theseus::instance::get_global_synced_options().await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_set_global_synced_option(
|
||||
option: InstanceSyncedOption,
|
||||
enabled: bool,
|
||||
) -> Result<theseus::instance::GlobalSyncedOptions> {
|
||||
Ok(theseus::instance::set_global_synced_option(option, enabled).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_get_command_history() -> Result<String> {
|
||||
Ok(theseus::instance::get_command_history().await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_set_command_history(contents: &str) -> Result<String> {
|
||||
Ok(theseus::instance::set_command_history(contents).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_open_synced_options_folder<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
) -> Result<()> {
|
||||
let path = theseus::instance::get_synced_options_folder().await?;
|
||||
app_handle
|
||||
.opener()
|
||||
.open_path(path.to_string_lossy(), None::<&str>)
|
||||
.map_err(|error| std::io::Error::other(error.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_list_synced_servers()
|
||||
-> Result<Vec<theseus::instance::SyncedServer>> {
|
||||
Ok(theseus::instance::list_synced_servers().await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_update_synced_server(
|
||||
server: theseus::instance::SyncedServer,
|
||||
) -> Result<()> {
|
||||
Ok(theseus::instance::update_synced_server(server).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_remove_synced_server(server_id: &str) -> Result<()> {
|
||||
Ok(theseus::instance::remove_synced_server(server_id).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_rebuild_synced_options(
|
||||
instance_id: Option<&str>,
|
||||
) -> Result<()> {
|
||||
if let Some(instance_id) = instance_id {
|
||||
theseus::instance::reconcile_instance_synced_options(instance_id)
|
||||
.await?;
|
||||
} else {
|
||||
theseus::instance::reconcile_all_synced_options().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_screenshots<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
screenshots: Vec<theseus::instance::InstanceScreenshot>,
|
||||
) -> Result<Vec<InstanceScreenshot>> {
|
||||
let mut result = Vec::with_capacity(screenshots.len());
|
||||
|
||||
for screenshot in screenshots {
|
||||
result.push(serialize_screenshot(app_handle, screenshot)?);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn serialize_screenshot<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
screenshot: theseus::instance::InstanceScreenshot,
|
||||
) -> Result<InstanceScreenshot> {
|
||||
app_handle
|
||||
.asset_protocol_scope()
|
||||
.allow_file(&screenshot.path)
|
||||
.map_err(|error| std::io::Error::other(error.to_string()))?;
|
||||
app_handle
|
||||
.fs_scope()
|
||||
.allow_file(&screenshot.path)
|
||||
.map_err(|error| std::io::Error::other(error.to_string()))?;
|
||||
let mut url = super::utils::tauri_convert_file_src(&screenshot.path)?;
|
||||
url.query_pairs_mut()
|
||||
.append_pair("revision", &screenshot.modified_at.to_string());
|
||||
|
||||
Ok(InstanceScreenshot {
|
||||
id: screenshot.id,
|
||||
instance_id: screenshot.instance_id,
|
||||
instance_name: screenshot.instance_name,
|
||||
file_name: screenshot.file_name,
|
||||
created_at: screenshot.created_at,
|
||||
modified_at: screenshot.modified_at,
|
||||
group_id: screenshot.group_id,
|
||||
path: screenshot.path,
|
||||
url,
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn instance_get_optimal_jre_key(
|
||||
instance_id: &str,
|
||||
|
||||
Reference in New Issue
Block a user