mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 20:18:12 +08:00
Add a recovery path for autosaved documents that don't open successfully (#4157)
* Add a recovery path for autosaved documents that don't open successfully * Fix file naming edge case * Add edge cases for Windows zip names * Simplify comments and remove desktop portion
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
pub struct FailedToLoadDocumentsDialog {
|
||||
pub failed_document_names: Vec<String>,
|
||||
}
|
||||
|
||||
impl DialogLayoutHolder for FailedToLoadDocumentsDialog {
|
||||
const ICON: &'static str = "Warning";
|
||||
const TITLE: &'static str = "Failed to Open Documents";
|
||||
|
||||
fn layout_buttons(&self) -> Layout {
|
||||
let widgets = vec![
|
||||
TextButton::new("Download")
|
||||
.emphasized(true)
|
||||
.tooltip_description("Save the raw document data to disk so it can be recovered later.")
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseAndThen {
|
||||
followups: vec![PortfolioMessage::DownloadFailedToLoadDocuments.into()],
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
TextButton::new("Discard")
|
||||
.tooltip_description("Permanently delete the autosaved data for these documents.")
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseAndThen {
|
||||
followups: vec![PortfolioMessage::DiscardFailedToLoadDocuments.into()],
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
TextButton::new("Dismiss")
|
||||
.tooltip_description("Close this dialog. The autosaved data is kept and this dialog will reappear on next launch.")
|
||||
.on_update(|_| FrontendMessage::DialogClose.into())
|
||||
.widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutHolder for FailedToLoadDocumentsDialog {
|
||||
fn layout(&self) -> Layout {
|
||||
let count = self.failed_document_names.len();
|
||||
let header = format!("{count} document{} couldn't be reopened.", if count == 1 { "" } else { "s" });
|
||||
let list = "• ".to_string() + &self.failed_document_names.join("\n• ");
|
||||
let plural_s = if count == 1 { "" } else { "s" };
|
||||
let plural_it_them = if count == 1 { "it" } else { "them" };
|
||||
|
||||
Layout(vec![
|
||||
LayoutGroup::row(vec![TextLabel::new(header).bold(true).multiline(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![
|
||||
TextLabel::new(format!(
|
||||
"Sorry about that!\n\
|
||||
This shouldn't happen, and we'd like to help.\n\
|
||||
\n\
|
||||
Click \"Download\" to save a copy of the affected file{plural_s},\n\
|
||||
then please share {plural_it_them} with us so we can investigate:"
|
||||
))
|
||||
.multiline(true)
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
TextButton::new("Ask on Discord")
|
||||
.icon("Volunteer")
|
||||
.flush(true)
|
||||
.on_update(|_| {
|
||||
FrontendMessage::TriggerVisitLink {
|
||||
url: "https://discord.graphite.art".into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
TextButton::new("Report on GitHub")
|
||||
.icon("Bug")
|
||||
.flush(true)
|
||||
.on_update(|_| {
|
||||
FrontendMessage::TriggerVisitLink {
|
||||
url: "https://github.com/GraphiteEditor/Graphite/issues/new".into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
TextLabel::new(
|
||||
"In the meantime, you can keep working in the\n\
|
||||
previous version of Graphite:",
|
||||
)
|
||||
.multiline(true)
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
TextButton::new("Sept. 2025 Release")
|
||||
.icon("GraphiteLogo")
|
||||
.flush(true)
|
||||
.on_update(|_| {
|
||||
FrontendMessage::TriggerVisitLink {
|
||||
url: "https://57130155.graphite.pages.dev/".into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![TextLabel::new(format!("Affected document{plural_s}:\n{list}")).multiline(true).widget_instance()]),
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
pub struct FailedToOpenDocumentDialog {
|
||||
pub document_name: String,
|
||||
}
|
||||
|
||||
impl DialogLayoutHolder for FailedToOpenDocumentDialog {
|
||||
const ICON: &'static str = "Warning";
|
||||
const TITLE: &'static str = "Failed to Open Document";
|
||||
|
||||
fn layout_buttons(&self) -> Layout {
|
||||
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutHolder for FailedToOpenDocumentDialog {
|
||||
fn layout(&self) -> Layout {
|
||||
let header = if self.document_name.trim().is_empty() {
|
||||
"The document couldn't be opened.".to_string()
|
||||
} else {
|
||||
format!("\"{}\" couldn't be opened.", self.document_name)
|
||||
};
|
||||
|
||||
Layout(vec![
|
||||
LayoutGroup::row(vec![TextLabel::new(header).bold(true).multiline(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![
|
||||
TextLabel::new(
|
||||
"Sorry about that!\n\
|
||||
This shouldn't happen, and we'd like to help.\n\
|
||||
\n\
|
||||
Please share the file with us so we can investigate:",
|
||||
)
|
||||
.multiline(true)
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
TextButton::new("Ask on Discord")
|
||||
.icon("Volunteer")
|
||||
.flush(true)
|
||||
.on_update(|_| {
|
||||
FrontendMessage::TriggerVisitLink {
|
||||
url: "https://discord.graphite.art".into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
TextButton::new("Report on GitHub")
|
||||
.icon("Bug")
|
||||
.flush(true)
|
||||
.on_update(|_| {
|
||||
FrontendMessage::TriggerVisitLink {
|
||||
url: "https://github.com/GraphiteEditor/Graphite/issues/new".into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
TextLabel::new(
|
||||
"In the meantime, you can keep working in the\n\
|
||||
previous version of Graphite:",
|
||||
)
|
||||
.multiline(true)
|
||||
.widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
TextButton::new("Sept. 2025 Release")
|
||||
.icon("GraphiteLogo")
|
||||
.flush(true)
|
||||
.on_update(|_| {
|
||||
FrontendMessage::TriggerVisitLink {
|
||||
url: "https://57130155.graphite.pages.dev/".into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
]),
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ mod close_document_dialog;
|
||||
mod confirm_restart_dialog;
|
||||
mod demo_artwork_dialog;
|
||||
mod error_dialog;
|
||||
mod failed_to_load_documents_dialog;
|
||||
mod failed_to_open_document_dialog;
|
||||
mod licenses_dialog;
|
||||
mod licenses_third_party_dialog;
|
||||
|
||||
@@ -14,5 +16,7 @@ pub use confirm_restart_dialog::ConfirmRestartDialog;
|
||||
pub use demo_artwork_dialog::ARTWORK;
|
||||
pub use demo_artwork_dialog::DemoArtworkDialog;
|
||||
pub use error_dialog::ErrorDialog;
|
||||
pub use failed_to_load_documents_dialog::FailedToLoadDocumentsDialog;
|
||||
pub use failed_to_open_document_dialog::FailedToOpenDocumentDialog;
|
||||
pub use licenses_dialog::LicensesDialog;
|
||||
pub use licenses_third_party_dialog::LicensesThirdPartyDialog;
|
||||
|
||||
Reference in New Issue
Block a user