Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 8 additions & 15 deletions desktop/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::render::{RenderError, RenderState};
use crate::ui::{InputEvent, UiCommand, UiInstance};
use crate::window::Window;
use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, IngestAction, Preferences};
use crate::wrapper::{DesktopWrapper, MmapResourceStorage, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};
use crate::wrapper::{DesktopWrapper, FolderStore, MmapResourceStorage, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};

pub(crate) struct App {
render_state: Option<RenderState>,
Expand Down Expand Up @@ -99,7 +99,13 @@ impl App {
let wake = Arc::new(move || {
wake_scheduler.schedule(AppEvent::DesktopWrapperMessage(DesktopWrapperMessage::Wake));
});
let desktop_wrapper = DesktopWrapper::new(rand::rng().random(), Arc::new(resource_storage), dirs::app_autosave_documents_dir(), wgpu_context.clone(), wake);
let desktop_wrapper = DesktopWrapper::new(
rand::rng().random(),
Arc::new(resource_storage),
Arc::new(FolderStore::new(dirs::app_autosave_documents_dir())),
wgpu_context.clone(),
wake,
);

Self {
render_state: None,
Expand Down Expand Up @@ -307,19 +313,6 @@ impl App {
DesktopFrontendMessage::PersistenceReadState => {
responses.push(DesktopWrapperMessage::LoadPersistedState { state: persist::read_state() });
}
DesktopFrontendMessage::PersistenceReadDocument { id } => {
if let Some(document) = persist::read_document_content(&id) {
responses.push(DesktopWrapperMessage::LoadDocumentContent { id, document });
} else {
tracing::error!("Failed to read document content for {id:?}");
}
}
DesktopFrontendMessage::PersistenceWriteDocument { id, document_serialized_content } => {
persist::write_document_content(id, document_serialized_content);
}
DesktopFrontendMessage::PersistenceDeleteDocument { id } => {
persist::delete_document(&id);
}
DesktopFrontendMessage::PersistenceWritePreferences { preferences } => {
preferences::write(preferences);
}
Expand Down
98 changes: 48 additions & 50 deletions desktop/src/persist.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::wrapper::messages::{DocumentId, PersistedState};
use crate::wrapper::messages::PersistedState;

pub(crate) fn read_state() -> PersistedState {
if let Err(error) = migrate_documents() {
tracing::error!("Autosave migration failed: {error}");
}
let path = state_file_path();
let data = match std::fs::read_to_string(&path) {
Ok(d) => d,
Expand All @@ -13,16 +16,13 @@ pub(crate) fn read_state() -> PersistedState {
return PersistedState::default();
}
};
let loaded = match ron::from_str(&data) {
match ron::from_str(&data) {
Ok(d) => d,
Err(e) => {
tracing::error!("Failed to deserialize persistent data: {e}");
return PersistedState::default();
PersistedState::default()
}
};

garbage_collect_document_files(&loaded);
loaded
}
}

pub(crate) fn write_state(state: PersistedState) {
Expand All @@ -37,57 +37,55 @@ pub(crate) fn write_state(state: PersistedState) {
if let Err(e) = std::fs::write(state_file_path(), data) {
tracing::error!("Failed to write persistent data to disk: {e}");
}
garbage_collect_document_files(state);
}

pub(crate) fn write_document_content(id: DocumentId, document_content: String) {
if let Err(e) = std::fs::write(document_content_path(&id), document_content) {
tracing::error!("Failed to write document {id:?} to disk: {e}");
}
}

pub(crate) fn read_document_content(id: &DocumentId) -> Option<String> {
std::fs::read_to_string(document_content_path(id)).ok()
}

pub(crate) fn delete_document(id: &DocumentId) {
if let Err(e) = std::fs::remove_file(document_content_path(id)) {
tracing::error!("Failed to delete document {id:?} from disk: {e}");
}
fn state_file_path() -> std::path::PathBuf {
let mut path = crate::dirs::app_data_dir();
path.push(crate::consts::APP_STATE_FILE_NAME);
path
}

fn garbage_collect_document_files(state: &PersistedState) {
let valid_paths: std::collections::HashSet<_> = state.documents.iter().map(|doc| document_content_path(&doc.id)).collect();

let directory = crate::dirs::app_autosave_documents_dir();
let entries = match std::fs::read_dir(&directory) {
// TODO: Remove this migration code
fn migrate_documents() -> std::io::Result<()> {
let root = crate::dirs::app_autosave_documents_dir();
let entries = match std::fs::read_dir(&root) {
Ok(entries) => entries,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return,
Err(e) => {
tracing::error!("Failed to read autosave documents directory: {e}");
return;
}
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(error) => return Err(error),
};

for entry in entries.flatten() {
let path = entry.path();
if path.is_file()
&& !valid_paths.contains(&path)
&& let Err(e) = std::fs::remove_file(&path)
{
tracing::error!("Failed to remove orphaned document file {path:?}: {e}");
for entry in entries {
let entry = match entry {
Ok(entry) => entry,
Err(error) => {
tracing::error!("Skipping an unreadable autosave entry: {error}");
continue;
}
};
if let Err(error) = migrate_document(&root, &entry) {
tracing::error!("Autosave migration of {} failed: {error}", entry.path().display());
}
}
Ok(())
}

fn state_file_path() -> std::path::PathBuf {
let mut path = crate::dirs::app_data_dir();
path.push(crate::consts::APP_STATE_FILE_NAME);
path
}

fn document_content_path(id: &DocumentId) -> std::path::PathBuf {
let mut path = crate::dirs::app_autosave_documents_dir();
path.push(format!("{:x}.{}", id.0, graphite_desktop_wrapper::FILE_EXTENSION));
path
fn migrate_document(root: &std::path::Path, entry: &std::fs::DirEntry) -> std::io::Result<()> {
let path = entry.path();
if !entry.file_type()?.is_file() || path.extension().and_then(|s| s.to_str()) != Some(graphite_desktop_wrapper::FILE_EXTENSION) {
return Ok(());
}
let Some(id) = path.file_stem().and_then(|s| s.to_str()).and_then(|s| u64::from_str_radix(s, 16).ok()) else {
return Ok(());
};
let destination = root.join(format!("{id:016x}"));
if !destination.join("legacy.graphite").exists() && !destination.join("manifest.json").exists() {
std::fs::create_dir_all(&destination)?;
let staging = destination.join("legacy.graphite.migrating");
std::fs::copy(&path, &staging)?;
std::fs::rename(staging, destination.join("legacy.graphite"))?;
}
let backup = path.with_extension(format!("{}.migrated", graphite_desktop_wrapper::FILE_EXTENSION));
if backup.exists() {
return Ok(());
}
std::fs::rename(path, backup)
}
4 changes: 0 additions & 4 deletions desktop/wrapper/src/handle_desktop_wrapper_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
let message = FrontendMessage::UpdateFullscreen { fullscreen };
dispatcher.queue_editor_message(message);
}
DesktopWrapperMessage::LoadDocumentContent { id, document } => {
let message = PersistentStateMessage::LoadDocument { document_id: id, document };
dispatcher.queue_editor_message(message);
}
DesktopWrapperMessage::LoadPersistedState { state } => {
let message = PersistentStateMessage::LoadState { state };
dispatcher.queue_editor_message(message);
Expand Down
12 changes: 0 additions & 12 deletions desktop/wrapper/src/intercept_frontend_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,6 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
FrontendMessage::TriggerPersistenceWriteState { state } => {
dispatcher.respond(DesktopFrontendMessage::PersistenceWriteState { state });
}
FrontendMessage::TriggerPersistenceReadDocument { document_id } => {
dispatcher.respond(DesktopFrontendMessage::PersistenceReadDocument { id: document_id });
}
FrontendMessage::TriggerPersistenceDeleteDocument { document_id } => {
dispatcher.respond(DesktopFrontendMessage::PersistenceDeleteDocument { id: document_id });
}
FrontendMessage::TriggerPersistenceWriteDocument { document_id, document } => {
dispatcher.respond(DesktopFrontendMessage::PersistenceWriteDocument {
id: document_id,
document_serialized_content: document,
});
}
FrontendMessage::TriggerOpenLaunchDocuments => {
dispatcher.respond(DesktopFrontendMessage::OpenLaunchDocuments);
}
Expand Down
5 changes: 3 additions & 2 deletions desktop/wrapper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::sync::Arc;

pub use graph_craft::application_io::resource::MmapResourceStorage;
pub use graphite_editor::consts::{DOUBLE_CLICK_MILLISECONDS, FILE_EXTENSION};
pub use graphite_editor::document_store::{DocumentStore, FolderStore};
pub use wgpu_executor::Texture;
pub use wgpu_executor::WgpuBackends;
pub use wgpu_executor::WgpuContext;
Expand All @@ -32,7 +33,7 @@ pub struct DesktopWrapper {
}

impl DesktopWrapper {
pub fn new(uuid_random_seed: u64, resource_storage: Arc<dyn ResourceStorage>, working_copy_root: std::path::PathBuf, wgpu_context: WgpuContext, schedule_wake: Wake) -> Self {
pub fn new(uuid_random_seed: u64, resource_storage: Arc<dyn ResourceStorage>, document_store: Arc<dyn DocumentStore>, wgpu_context: WgpuContext, schedule_wake: Wake) -> Self {
#[cfg(target_os = "windows")]
let host = Host::Windows;
#[cfg(target_os = "macos")]
Expand All @@ -43,7 +44,7 @@ impl DesktopWrapper {
let application_io = PlatformApplicationIo::new_with_context(wgpu_context);

Self {
editor: Editor::new(env, uuid_random_seed, resource_storage, Some(working_copy_root), application_io, schedule_wake),
editor: Editor::new(env, uuid_random_seed, resource_storage, document_store, application_io, schedule_wake),
}
}

Expand Down
11 changes: 0 additions & 11 deletions desktop/wrapper/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ pub enum DesktopFrontendMessage {
enabled: bool,
},
UpdateOverlays(vello::Scene),
PersistenceWriteDocument {
id: DocumentId,
document_serialized_content: String,
},
PersistenceDeleteDocument {
id: DocumentId,
},
PersistenceWritePreferences {
preferences: Preferences,
},
Expand All @@ -59,9 +52,6 @@ pub enum DesktopFrontendMessage {
state: PersistedState,
},
PersistenceReadState,
PersistenceReadDocument {
id: DocumentId,
},
UpdateMenu {
entries: Vec<MenuItem>,
},
Expand Down Expand Up @@ -92,7 +82,6 @@ pub enum DesktopWrapperMessage {
PollNodeGraphEvaluation,
UpdateMaximized { maximized: bool },
UpdateFullscreen { fullscreen: bool },
LoadDocumentContent { id: DocumentId, document: String },
LoadPersistedState { state: PersistedState },
LoadPreferences { preferences: Preferences },
MenuEvent { id: String },
Expand Down
1 change: 1 addition & 0 deletions document/container/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ web-sys = { workspace = true, features = [
"FileSystemFileHandle",
"FileSystemGetFileOptions",
"FileSystemGetDirectoryOptions",
"FileSystemRemoveOptions",
"FileSystemHandle",
"FileSystemHandleKind",
"FileSystemWritableFileStream",
Expand Down
1 change: 1 addition & 0 deletions document/container/src/backends/folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};

#[derive(Clone)]
pub struct FolderBackend {
root: PathBuf,
}
Expand Down
6 changes: 3 additions & 3 deletions document/container/src/backends/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

use crate::{ByteHolder, Container, ContainerError, Result, validate_path, validate_prefix, with_trailing_slash};
use std::collections::{HashMap, HashSet};
use std::sync::Mutex;
use std::sync::{Arc, Mutex};

#[derive(Default)]
#[derive(Clone, Default)]
pub struct MemoryBackend {
files: Mutex<HashMap<String, Vec<u8>>>,
files: Arc<Mutex<HashMap<String, Vec<u8>>>>,
}

impl MemoryBackend {
Expand Down
Loading
Loading