Clean up autosave persistence (#3115)
* Set auto save state to false on document rename * Update open document list on transaction commit and aboard * Use current network to compute hash Was using the last element in undo Before artworks where not auto saved when the had no undo history * Refactor persistence
This commit is contained in:
parent
b5ebe78f5e
commit
083dfa5f49
|
|
@ -1,4 +1,4 @@
|
||||||
use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon};
|
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
||||||
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{
|
use crate::messages::portfolio::document::node_graph::utility_types::{
|
||||||
|
|
@ -90,13 +90,15 @@ pub enum FrontendMessage {
|
||||||
font: Font,
|
font: Font,
|
||||||
},
|
},
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerIndexedDbRemoveDocument {
|
TriggerPersistenceRemoveDocument {
|
||||||
#[serde(rename = "documentId")]
|
#[serde(rename = "documentId")]
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
},
|
},
|
||||||
TriggerIndexedDbWriteDocument {
|
TriggerPersistenceWriteDocument {
|
||||||
|
#[serde(rename = "documentId")]
|
||||||
|
document_id: DocumentId,
|
||||||
document: String,
|
document: String,
|
||||||
details: FrontendDocumentDetails,
|
details: DocumentDetails,
|
||||||
},
|
},
|
||||||
TriggerLoadFirstAutoSaveDocument,
|
TriggerLoadFirstAutoSaveDocument,
|
||||||
TriggerLoadRestAutoSaveDocuments,
|
TriggerLoadRestAutoSaveDocuments,
|
||||||
|
|
@ -308,7 +310,7 @@ pub enum FrontendMessage {
|
||||||
},
|
},
|
||||||
UpdateOpenDocumentsList {
|
UpdateOpenDocumentsList {
|
||||||
#[serde(rename = "openDocuments")]
|
#[serde(rename = "openDocuments")]
|
||||||
open_documents: Vec<FrontendDocumentDetails>,
|
open_documents: Vec<OpenDocument>,
|
||||||
},
|
},
|
||||||
UpdatePropertiesPanelLayout {
|
UpdatePropertiesPanelLayout {
|
||||||
#[serde(rename = "layoutTarget")]
|
#[serde(rename = "layoutTarget")]
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,18 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct FrontendDocumentDetails {
|
pub struct OpenDocument {
|
||||||
#[serde(rename = "isAutoSaved")]
|
pub id: DocumentId,
|
||||||
pub is_auto_saved: bool,
|
pub details: DocumentDetails,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct DocumentDetails {
|
||||||
|
pub name: String,
|
||||||
#[serde(rename = "isSaved")]
|
#[serde(rename = "isSaved")]
|
||||||
pub is_saved: bool,
|
pub is_saved: bool,
|
||||||
pub name: String,
|
#[serde(rename = "isAutoSaved")]
|
||||||
pub id: DocumentId,
|
pub is_auto_saved: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||||
|
|
||||||
self.path = None;
|
self.path = None;
|
||||||
self.set_save_state(false);
|
self.set_save_state(false);
|
||||||
|
self.set_auto_save_state(false);
|
||||||
|
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||||
|
|
@ -1301,6 +1302,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||||
}
|
}
|
||||||
self.network_interface.finish_transaction();
|
self.network_interface.finish_transaction();
|
||||||
self.document_redo_history.clear();
|
self.document_redo_history.clear();
|
||||||
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
}
|
}
|
||||||
DocumentMessage::AbortTransaction => {
|
DocumentMessage::AbortTransaction => {
|
||||||
responses.add(DocumentMessage::RepeatedAbortTransaction { undo_count: 1 });
|
responses.add(DocumentMessage::RepeatedAbortTransaction { undo_count: 1 });
|
||||||
|
|
@ -1316,6 +1318,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||||
|
|
||||||
self.network_interface.finish_transaction();
|
self.network_interface.finish_transaction();
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
}
|
}
|
||||||
DocumentMessage::ToggleLayerExpansion { id, recursive } => {
|
DocumentMessage::ToggleLayerExpansion { id, recursive } => {
|
||||||
let layer = LayerNodeIdentifier::new(id, &self.network_interface);
|
let layer = LayerNodeIdentifier::new(id, &self.network_interface);
|
||||||
|
|
@ -1975,16 +1978,16 @@ impl DocumentMessageHandler {
|
||||||
Some(previous_network)
|
Some(previous_network)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_hash(&self) -> Option<u64> {
|
pub fn current_hash(&self) -> u64 {
|
||||||
self.document_undo_history.iter().last().map(|network| network.document_network().current_hash())
|
self.network_interface.document_network().current_hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_auto_saved(&self) -> bool {
|
pub fn is_auto_saved(&self) -> bool {
|
||||||
self.current_hash() == self.auto_saved_hash
|
Some(self.current_hash()) == self.auto_saved_hash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_saved(&self) -> bool {
|
pub fn is_saved(&self) -> bool {
|
||||||
self.current_hash() == self.saved_hash
|
Some(self.current_hash()) == self.saved_hash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_graph_overlay_open(&self) -> bool {
|
pub fn is_graph_overlay_open(&self) -> bool {
|
||||||
|
|
@ -1993,7 +1996,7 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
pub fn set_auto_save_state(&mut self, is_saved: bool) {
|
pub fn set_auto_save_state(&mut self, is_saved: bool) {
|
||||||
if is_saved {
|
if is_saved {
|
||||||
self.auto_saved_hash = self.current_hash();
|
self.auto_saved_hash = Some(self.current_hash());
|
||||||
} else {
|
} else {
|
||||||
self.auto_saved_hash = None;
|
self.auto_saved_hash = None;
|
||||||
}
|
}
|
||||||
|
|
@ -2001,7 +2004,7 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
pub fn set_save_state(&mut self, is_saved: bool) {
|
pub fn set_save_state(&mut self, is_saved: bool) {
|
||||||
if is_saved {
|
if is_saved {
|
||||||
self.saved_hash = self.current_hash();
|
self.saved_hash = Some(self.current_hash());
|
||||||
} else {
|
} else {
|
||||||
self.saved_hash = None;
|
self.saved_hash = None;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::consts::{DEFAULT_DOCUMENT_NAME, DEFAULT_STROKE_WIDTH, FILE_EXTENSION}
|
||||||
use crate::messages::animation::TimingInformation;
|
use crate::messages::animation::TimingInformation;
|
||||||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
||||||
use crate::messages::dialog::simple_dialogs;
|
use crate::messages::dialog::simple_dialogs;
|
||||||
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
|
use crate::messages::frontend::utility_types::{DocumentDetails, OpenDocument};
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::DocumentMessageContext;
|
use crate::messages::portfolio::document::DocumentMessageContext;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
|
|
@ -187,13 +187,13 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||||
}
|
}
|
||||||
PortfolioMessage::AutoSaveDocument { document_id } => {
|
PortfolioMessage::AutoSaveDocument { document_id } => {
|
||||||
let document = self.documents.get(&document_id).unwrap();
|
let document = self.documents.get(&document_id).unwrap();
|
||||||
responses.add(FrontendMessage::TriggerIndexedDbWriteDocument {
|
responses.add(FrontendMessage::TriggerPersistenceWriteDocument {
|
||||||
|
document_id,
|
||||||
document: document.serialize_document(),
|
document: document.serialize_document(),
|
||||||
details: FrontendDocumentDetails {
|
details: DocumentDetails {
|
||||||
is_auto_saved: document.is_auto_saved(),
|
|
||||||
is_saved: document.is_saved(),
|
|
||||||
id: document_id,
|
|
||||||
name: document.name.clone(),
|
name: document.name.clone(),
|
||||||
|
is_saved: document.is_saved(),
|
||||||
|
is_auto_saved: document.is_auto_saved(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +216,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||||
}
|
}
|
||||||
|
|
||||||
for document_id in &self.document_ids {
|
for document_id in &self.document_ids {
|
||||||
responses.add(FrontendMessage::TriggerIndexedDbRemoveDocument { document_id: *document_id });
|
responses.add(FrontendMessage::TriggerPersistenceRemoveDocument { document_id: *document_id });
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(PortfolioMessage::DestroyAllDocuments);
|
responses.add(PortfolioMessage::DestroyAllDocuments);
|
||||||
|
|
@ -242,7 +242,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||||
|
|
||||||
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
|
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
|
||||||
responses.add(PortfolioMessage::DeleteDocument { document_id });
|
responses.add(PortfolioMessage::DeleteDocument { document_id });
|
||||||
responses.add(FrontendMessage::TriggerIndexedDbRemoveDocument { document_id });
|
responses.add(FrontendMessage::TriggerPersistenceRemoveDocument { document_id });
|
||||||
|
|
||||||
// Send the new list of document tab names
|
// Send the new list of document tab names
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
|
|
@ -1044,11 +1044,13 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||||
.document_ids
|
.document_ids
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|id| {
|
.filter_map(|id| {
|
||||||
self.documents.get(id).map(|document| FrontendDocumentDetails {
|
self.documents.get(id).map(|document| OpenDocument {
|
||||||
is_auto_saved: document.is_auto_saved(),
|
|
||||||
is_saved: document.is_saved(),
|
|
||||||
id: *id,
|
id: *id,
|
||||||
name: document.name.clone(),
|
details: DocumentDetails {
|
||||||
|
is_auto_saved: document.is_auto_saved(),
|
||||||
|
is_saved: document.is_saved(),
|
||||||
|
name: document.name.clone(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
import type { Editor } from "@graphite/editor";
|
import type { Editor } from "@graphite/editor";
|
||||||
import type { FrontendDocumentDetails } from "@graphite/messages";
|
import type { OpenDocument } from "@graphite/messages";
|
||||||
import type { DialogState } from "@graphite/state-providers/dialog";
|
import type { DialogState } from "@graphite/state-providers/dialog";
|
||||||
import type { PortfolioState } from "@graphite/state-providers/portfolio";
|
import type { PortfolioState } from "@graphite/state-providers/portfolio";
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
$: documentPanel?.scrollTabIntoView($portfolio.activeDocumentIndex);
|
$: documentPanel?.scrollTabIntoView($portfolio.activeDocumentIndex);
|
||||||
|
|
||||||
$: documentTabLabels = $portfolio.documents.map((doc: FrontendDocumentDetails) => {
|
$: documentTabLabels = $portfolio.documents.map((doc: OpenDocument) => {
|
||||||
const name = doc.displayName;
|
const name = doc.displayName;
|
||||||
|
|
||||||
if (!editor.handle.inDevelopmentMode()) return { name };
|
if (!editor.handle.inDevelopmentMode()) return { name };
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
|
|
||||||
async function onBeforeUnload(e: BeforeUnloadEvent) {
|
async function onBeforeUnload(e: BeforeUnloadEvent) {
|
||||||
const activeDocument = get(portfolio).documents[get(portfolio).activeDocumentIndex];
|
const activeDocument = get(portfolio).documents[get(portfolio).activeDocumentIndex];
|
||||||
if (activeDocument && !activeDocument.isAutoSaved) editor.handle.triggerAutoSave(activeDocument.id);
|
if (activeDocument && !activeDocument.details.isAutoSaved) editor.handle.triggerAutoSave(activeDocument.id);
|
||||||
|
|
||||||
// Skip the message if the editor crashed, since work is already lost
|
// Skip the message if the editor crashed, since work is already lost
|
||||||
if (await editor.handle.hasCrashed()) return;
|
if (await editor.handle.hasCrashed()) return;
|
||||||
|
|
@ -291,7 +291,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
// Skip the message during development, since it's annoying when testing
|
// Skip the message during development, since it's annoying when testing
|
||||||
if (await editor.handle.inDevelopmentMode()) return;
|
if (await editor.handle.inDevelopmentMode()) return;
|
||||||
|
|
||||||
const allDocumentsSaved = get(portfolio).documents.reduce((acc, doc) => acc && doc.isSaved, true);
|
const allDocumentsSaved = get(portfolio).documents.reduce((acc, doc) => acc && doc.details.isSaved, true);
|
||||||
if (!allDocumentsSaved) {
|
if (!allDocumentsSaved) {
|
||||||
e.returnValue = "Unsaved work will be lost if the web browser tab is closed. Close anyway?";
|
e.returnValue = "Unsaved work will be lost if the web browser tab is closed. Close anyway?";
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ import { get as getFromStore } from "svelte/store";
|
||||||
|
|
||||||
import { type Editor } from "@graphite/editor";
|
import { type Editor } from "@graphite/editor";
|
||||||
import {
|
import {
|
||||||
TriggerIndexedDbWriteDocument,
|
TriggerPersistenceWriteDocument,
|
||||||
TriggerIndexedDbRemoveDocument,
|
TriggerPersistenceRemoveDocument,
|
||||||
TriggerSavePreferences,
|
TriggerSavePreferences,
|
||||||
TriggerLoadPreferences,
|
TriggerLoadPreferences,
|
||||||
TriggerLoadFirstAutoSaveDocument,
|
TriggerLoadFirstAutoSaveDocument,
|
||||||
|
|
@ -27,23 +27,23 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
||||||
await set("current_document_id", String(documentId), graphiteStore);
|
await set("current_document_id", String(documentId), graphiteStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function storeDocument(autoSaveDocument: TriggerIndexedDbWriteDocument) {
|
async function storeDocument(autoSaveDocument: TriggerPersistenceWriteDocument) {
|
||||||
await update<Record<string, TriggerIndexedDbWriteDocument>>(
|
await update<Record<string, TriggerPersistenceWriteDocument>>(
|
||||||
"documents",
|
"documents",
|
||||||
(old) => {
|
(old) => {
|
||||||
const documents = old || {};
|
const documents = old || {};
|
||||||
documents[autoSaveDocument.details.id] = autoSaveDocument;
|
documents[autoSaveDocument.documentId] = autoSaveDocument;
|
||||||
return documents;
|
return documents;
|
||||||
},
|
},
|
||||||
graphiteStore,
|
graphiteStore,
|
||||||
);
|
);
|
||||||
|
|
||||||
await storeDocumentOrder();
|
await storeDocumentOrder();
|
||||||
await storeCurrentDocumentId(autoSaveDocument.details.id);
|
await storeCurrentDocumentId(autoSaveDocument.documentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeDocument(id: string) {
|
async function removeDocument(id: string) {
|
||||||
await update<Record<string, TriggerIndexedDbWriteDocument>>(
|
await update<Record<string, TriggerPersistenceWriteDocument>>(
|
||||||
"documents",
|
"documents",
|
||||||
(old) => {
|
(old) => {
|
||||||
const documents = old || {};
|
const documents = old || {};
|
||||||
|
|
@ -77,7 +77,7 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFirstDocument() {
|
async function loadFirstDocument() {
|
||||||
const previouslySavedDocuments = await get<Record<string, TriggerIndexedDbWriteDocument>>("documents", graphiteStore);
|
const previouslySavedDocuments = await get<Record<string, TriggerPersistenceWriteDocument>>("documents", graphiteStore);
|
||||||
const documentOrder = await get<string[]>("documents_tab_order", graphiteStore);
|
const documentOrder = await get<string[]>("documents_tab_order", graphiteStore);
|
||||||
const currentDocumentId = await get<string>("current_document_id", graphiteStore);
|
const currentDocumentId = await get<string>("current_document_id", graphiteStore);
|
||||||
if (!previouslySavedDocuments || !documentOrder) return;
|
if (!previouslySavedDocuments || !documentOrder) return;
|
||||||
|
|
@ -86,20 +86,20 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
||||||
|
|
||||||
if (currentDocumentId && currentDocumentId in previouslySavedDocuments) {
|
if (currentDocumentId && currentDocumentId in previouslySavedDocuments) {
|
||||||
const doc = previouslySavedDocuments[currentDocumentId];
|
const doc = previouslySavedDocuments[currentDocumentId];
|
||||||
editor.handle.openAutoSavedDocument(BigInt(doc.details.id), doc.details.name, doc.details.isSaved, doc.document, false);
|
editor.handle.openAutoSavedDocument(BigInt(doc.documentId), doc.details.name, doc.details.isSaved, doc.document, false);
|
||||||
editor.handle.selectDocument(BigInt(currentDocumentId));
|
editor.handle.selectDocument(BigInt(currentDocumentId));
|
||||||
} else {
|
} else {
|
||||||
const len = orderedSavedDocuments.length;
|
const len = orderedSavedDocuments.length;
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
const doc = orderedSavedDocuments[len - 1];
|
const doc = orderedSavedDocuments[len - 1];
|
||||||
editor.handle.openAutoSavedDocument(BigInt(doc.details.id), doc.details.name, doc.details.isSaved, doc.document, false);
|
editor.handle.openAutoSavedDocument(BigInt(doc.documentId), doc.details.name, doc.details.isSaved, doc.document, false);
|
||||||
editor.handle.selectDocument(BigInt(doc.details.id));
|
editor.handle.selectDocument(BigInt(doc.documentId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadRestDocuments() {
|
async function loadRestDocuments() {
|
||||||
const previouslySavedDocuments = await get<Record<string, TriggerIndexedDbWriteDocument>>("documents", graphiteStore);
|
const previouslySavedDocuments = await get<Record<string, TriggerPersistenceWriteDocument>>("documents", graphiteStore);
|
||||||
const documentOrder = await get<string[]>("documents_tab_order", graphiteStore);
|
const documentOrder = await get<string[]>("documents_tab_order", graphiteStore);
|
||||||
const currentDocumentId = await get<string>("current_document_id", graphiteStore);
|
const currentDocumentId = await get<string>("current_document_id", graphiteStore);
|
||||||
if (!previouslySavedDocuments || !documentOrder) return;
|
if (!previouslySavedDocuments || !documentOrder) return;
|
||||||
|
|
@ -107,19 +107,19 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
||||||
const orderedSavedDocuments = documentOrder.flatMap((id) => (previouslySavedDocuments[id] ? [previouslySavedDocuments[id]] : []));
|
const orderedSavedDocuments = documentOrder.flatMap((id) => (previouslySavedDocuments[id] ? [previouslySavedDocuments[id]] : []));
|
||||||
|
|
||||||
if (currentDocumentId) {
|
if (currentDocumentId) {
|
||||||
const currentIndex = orderedSavedDocuments.findIndex((doc) => doc.details.id === currentDocumentId);
|
const currentIndex = orderedSavedDocuments.findIndex((doc) => doc.documentId === currentDocumentId);
|
||||||
const beforeCurrentIndex = currentIndex - 1;
|
const beforeCurrentIndex = currentIndex - 1;
|
||||||
const afterCurrentIndex = currentIndex + 1;
|
const afterCurrentIndex = currentIndex + 1;
|
||||||
|
|
||||||
for (let i = beforeCurrentIndex; i >= 0; i--) {
|
for (let i = beforeCurrentIndex; i >= 0; i--) {
|
||||||
const { document, details } = orderedSavedDocuments[i];
|
const { documentId, document, details } = orderedSavedDocuments[i];
|
||||||
const { id, name, isSaved } = details;
|
const { name, isSaved } = details;
|
||||||
editor.handle.openAutoSavedDocument(BigInt(id), name, isSaved, document, true);
|
editor.handle.openAutoSavedDocument(BigInt(documentId), name, isSaved, document, true);
|
||||||
}
|
}
|
||||||
for (let i = afterCurrentIndex; i < orderedSavedDocuments.length; i++) {
|
for (let i = afterCurrentIndex; i < orderedSavedDocuments.length; i++) {
|
||||||
const { document, details } = orderedSavedDocuments[i];
|
const { documentId, document, details } = orderedSavedDocuments[i];
|
||||||
const { id, name, isSaved } = details;
|
const { name, isSaved } = details;
|
||||||
editor.handle.openAutoSavedDocument(BigInt(id), name, isSaved, document, false);
|
editor.handle.openAutoSavedDocument(BigInt(documentId), name, isSaved, document, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.handle.selectDocument(BigInt(currentDocumentId));
|
editor.handle.selectDocument(BigInt(currentDocumentId));
|
||||||
|
|
@ -127,13 +127,13 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
||||||
const length = orderedSavedDocuments.length;
|
const length = orderedSavedDocuments.length;
|
||||||
|
|
||||||
for (let i = length - 2; i >= 0; i--) {
|
for (let i = length - 2; i >= 0; i--) {
|
||||||
const { document, details } = orderedSavedDocuments[i];
|
const { documentId, document, details } = orderedSavedDocuments[i];
|
||||||
const { id, name, isSaved } = details;
|
const { name, isSaved } = details;
|
||||||
editor.handle.openAutoSavedDocument(BigInt(id), name, isSaved, document, true);
|
editor.handle.openAutoSavedDocument(BigInt(documentId), name, isSaved, document, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
const id = orderedSavedDocuments[length - 1].details.id;
|
const id = orderedSavedDocuments[length - 1].documentId;
|
||||||
editor.handle.selectDocument(BigInt(id));
|
editor.handle.selectDocument(BigInt(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -161,10 +161,10 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerLoadPreferences, async () => {
|
editor.subscriptions.subscribeJsMessage(TriggerLoadPreferences, async () => {
|
||||||
await loadPreferences();
|
await loadPreferences();
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerIndexedDbWriteDocument, async (autoSaveDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerPersistenceWriteDocument, async (autoSaveDocument) => {
|
||||||
await storeDocument(autoSaveDocument);
|
await storeDocument(autoSaveDocument);
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerIndexedDbRemoveDocument, async (removeAutoSaveDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerPersistenceRemoveDocument, async (removeAutoSaveDocument) => {
|
||||||
await removeDocument(removeAutoSaveDocument.documentId);
|
await removeDocument(removeAutoSaveDocument.documentId);
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerLoadFirstAutoSaveDocument, async () => {
|
editor.subscriptions.subscribeJsMessage(TriggerLoadFirstAutoSaveDocument, async () => {
|
||||||
|
|
@ -175,7 +175,7 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerSaveActiveDocument, async (triggerSaveActiveDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerSaveActiveDocument, async (triggerSaveActiveDocument) => {
|
||||||
const documentId = String(triggerSaveActiveDocument.documentId);
|
const documentId = String(triggerSaveActiveDocument.documentId);
|
||||||
const previouslySavedDocuments = await get<Record<string, TriggerIndexedDbWriteDocument>>("documents", graphiteStore);
|
const previouslySavedDocuments = await get<Record<string, TriggerPersistenceWriteDocument>>("documents", graphiteStore);
|
||||||
if (!previouslySavedDocuments) return;
|
if (!previouslySavedDocuments) return;
|
||||||
if (documentId in previouslySavedDocuments) {
|
if (documentId in previouslySavedDocuments) {
|
||||||
await storeCurrentDocumentId(documentId);
|
await storeCurrentDocumentId(documentId);
|
||||||
|
|
|
||||||
|
|
@ -129,37 +129,36 @@ export class UpdateNodeGraphSelection extends JsMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateOpenDocumentsList extends JsMessage {
|
export class UpdateOpenDocumentsList extends JsMessage {
|
||||||
@Type(() => FrontendDocumentDetails)
|
@Type(() => OpenDocument)
|
||||||
readonly openDocuments!: FrontendDocumentDetails[];
|
readonly openDocuments!: OpenDocument[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateWirePathInProgress extends JsMessage {
|
export class UpdateWirePathInProgress extends JsMessage {
|
||||||
readonly wirePath!: WirePath | undefined;
|
readonly wirePath!: WirePath | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows the auto save system to use a string for the id rather than a BigInt.
|
export class OpenDocument {
|
||||||
// IndexedDb does not allow for BigInts as primary keys.
|
readonly id!: bigint;
|
||||||
// TypeScript does not allow subclasses to change the type of class variables in subclasses.
|
@Type(() => DocumentDetails)
|
||||||
// It is an abstract class to point out that it should not be instantiated directly.
|
readonly details!: DocumentDetails;
|
||||||
export abstract class DocumentDetails {
|
|
||||||
|
get displayName(): string {
|
||||||
|
return this.details.displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DocumentDetails {
|
||||||
readonly name!: string;
|
readonly name!: string;
|
||||||
|
|
||||||
readonly isAutoSaved!: boolean;
|
readonly isAutoSaved!: boolean;
|
||||||
|
|
||||||
readonly isSaved!: boolean;
|
readonly isSaved!: boolean;
|
||||||
|
|
||||||
// This field must be provided by the subclass implementation
|
|
||||||
// readonly id!: bigint | string;
|
|
||||||
|
|
||||||
get displayName(): string {
|
get displayName(): string {
|
||||||
return `${this.name}${this.isSaved ? "" : "*"}`;
|
return `${this.name}${this.isSaved ? "" : "*"}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FrontendDocumentDetails extends DocumentDetails {
|
|
||||||
readonly id!: bigint;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Box {
|
export class Box {
|
||||||
readonly startX!: number;
|
readonly startX!: number;
|
||||||
|
|
||||||
|
|
@ -277,21 +276,20 @@ export class WireUpdate {
|
||||||
readonly wirePathUpdate!: WirePath | undefined;
|
readonly wirePathUpdate!: WirePath | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IndexedDbDocumentDetails extends DocumentDetails {
|
export class TriggerPersistenceWriteDocument extends JsMessage {
|
||||||
|
// Use a string since IndexedDB can not use BigInts for keys
|
||||||
@Transform(({ value }: { value: bigint }) => value.toString())
|
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||||
id!: string;
|
documentId!: string;
|
||||||
}
|
|
||||||
|
|
||||||
export class TriggerIndexedDbWriteDocument extends JsMessage {
|
|
||||||
document!: string;
|
document!: string;
|
||||||
|
|
||||||
@Type(() => IndexedDbDocumentDetails)
|
@Type(() => DocumentDetails)
|
||||||
details!: IndexedDbDocumentDetails;
|
details!: DocumentDetails;
|
||||||
|
|
||||||
version!: string;
|
version!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerIndexedDbRemoveDocument extends JsMessage {
|
export class TriggerPersistenceRemoveDocument extends JsMessage {
|
||||||
// Use a string since IndexedDB can not use BigInts for keys
|
// Use a string since IndexedDB can not use BigInts for keys
|
||||||
@Transform(({ value }: { value: bigint }) => value.toString())
|
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||||
documentId!: string;
|
documentId!: string;
|
||||||
|
|
@ -1643,8 +1641,8 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||||
TriggerFetchAndOpenDocument,
|
TriggerFetchAndOpenDocument,
|
||||||
TriggerFontLoad,
|
TriggerFontLoad,
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerIndexedDbRemoveDocument,
|
TriggerPersistenceRemoveDocument,
|
||||||
TriggerIndexedDbWriteDocument,
|
TriggerPersistenceWriteDocument,
|
||||||
TriggerLoadFirstAutoSaveDocument,
|
TriggerLoadFirstAutoSaveDocument,
|
||||||
TriggerLoadPreferences,
|
TriggerLoadPreferences,
|
||||||
TriggerLoadRestAutoSaveDocuments,
|
TriggerLoadRestAutoSaveDocuments,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
import { type Editor } from "@graphite/editor";
|
import { type Editor } from "@graphite/editor";
|
||||||
|
import type { OpenDocument } from "@graphite/messages";
|
||||||
import {
|
import {
|
||||||
type FrontendDocumentDetails,
|
|
||||||
TriggerFetchAndOpenDocument,
|
TriggerFetchAndOpenDocument,
|
||||||
TriggerSaveDocument,
|
TriggerSaveDocument,
|
||||||
TriggerExportImage,
|
TriggerExportImage,
|
||||||
|
|
@ -21,7 +21,7 @@ import { extractPixelData, rasterizeSVG } from "@graphite/utility-functions/rast
|
||||||
export function createPortfolioState(editor: Editor) {
|
export function createPortfolioState(editor: Editor) {
|
||||||
const { subscribe, update } = writable({
|
const { subscribe, update } = writable({
|
||||||
unsaved: false,
|
unsaved: false,
|
||||||
documents: [] as FrontendDocumentDetails[],
|
documents: [] as OpenDocument[],
|
||||||
activeDocumentIndex: 0,
|
activeDocumentIndex: 0,
|
||||||
dataPanelOpen: false,
|
dataPanelOpen: false,
|
||||||
propertiesPanelOpen: true,
|
propertiesPanelOpen: true,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue