Implement artboards and document version enforcement (#466)

* - graphite document artboard implementation
- autosave document load hitch fix
- Autosave will delete saved files when graphite document version changes

* formating

* - top left 0,0
- fixed hitch on first document
- vue calls first render

* Revert

* Merge branch 'master' into artboards

* Small bug fixes and code review tweaks

Co-authored-by: Oliver Davies <oliver@psyfer.io>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
mfish33
2022-01-08 07:50:08 -08:00
committed by Keavon Chambers
co-authored by Oliver Davies Keavon Chambers
parent 8fedfb603e
commit effc8354d1
17 changed files with 218 additions and 33 deletions
+12 -7
View File
@@ -124,6 +124,7 @@
</LayoutCol>
<LayoutCol :class="'canvas-area'">
<div class="canvas" ref="canvas">
<svg class="artboards" v-html="artboardSvg" :style="{ width: canvasSvgWidth, height: canvasSvgHeight }"></svg>
<svg class="artwork" v-html="artworkSvg" :style="{ width: canvasSvgWidth, height: canvasSvgHeight }"></svg>
<svg class="overlays" v-html="overlaysSvg" :style="{ width: canvasSvgWidth, height: canvasSvgHeight }"></svg>
</div>
@@ -233,13 +234,12 @@
// Fallback values if JS hasn't set these to integers yet
width: 100%;
height: 100%;
// Allows dev tools to select the artwork without being blocked by the SVG containers
pointer-events: none;
&.artwork {
background: #ffffff;
}
&.overlays {
user-select: none;
// Prevent inheritance from reaching the child elements
> * {
pointer-events: auto;
}
}
}
@@ -251,7 +251,7 @@
<script lang="ts">
import { defineComponent } from "vue";
import { UpdateArtwork, UpdateOverlays, UpdateScrollbars, UpdateRulers, SetActiveTool, SetCanvasZoom, SetCanvasRotation, ToolName } from "@/dispatcher/js-messages";
import { UpdateArtwork, UpdateOverlays, UpdateScrollbars, UpdateRulers, SetActiveTool, SetCanvasZoom, SetCanvasRotation, ToolName, UpdateArtboards } from "@/dispatcher/js-messages";
import LayoutCol from "@/components/layout/LayoutCol.vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
@@ -338,6 +338,10 @@ export default defineComponent({
this.overlaysSvg = updateOverlays.svg;
});
this.editor.dispatcher.subscribeJsMessage(UpdateArtboards, (updateArtboards) => {
this.artboardSvg = updateArtboards.svg;
});
this.editor.dispatcher.subscribeJsMessage(UpdateScrollbars, (updateScrollbars) => {
this.scrollbarPos = updateScrollbars.position;
this.scrollbarSize = updateScrollbars.size;
@@ -383,6 +387,7 @@ export default defineComponent({
return {
artworkSvg: "",
artboardSvg: "",
overlaysSvg: "",
canvasSvgWidth: "100%",
canvasSvgHeight: "100%",
@@ -65,8 +65,16 @@ function makeMenuEntries(editor: EditorState): MenuListEntries {
ref: undefined,
children: [
[
{ label: "New", icon: "File", shortcut: ["KeyControl", "KeyN"], shortcutRequiresLock: true, action: async (): Promise<void> => editor.instance.new_document() },
{ label: "Open…", shortcut: ["KeyControl", "KeyO"], action: async (): Promise<void> => editor.instance.open_document() },
{ label: "New", icon: "File", shortcut: ["KeyControl", "KeyN"], shortcutRequiresLock: true, action: (): void => editor.instance.new_document() },
{
label: "New 1920x1080",
icon: "File",
action: (): void => {
editor.instance.new_document();
editor.instance.create_artboard(0, 0, 1920, 1080);
},
},
{ label: "Open…", shortcut: ["KeyControl", "KeyO"], action: (): void => editor.instance.open_document() },
{
label: "Open Recent",
shortcut: ["KeyControl", "KeyShift", "KeyO"],
+7
View File
@@ -175,6 +175,10 @@ export class UpdateOverlays extends JsMessage {
readonly svg!: string;
}
export class UpdateArtboards extends JsMessage {
readonly svg!: string;
}
const TupleToVec2 = Transform(({ value }) => ({ x: value[0], y: value[1] }));
export class UpdateScrollbars extends JsMessage {
@@ -349,6 +353,8 @@ export class AutoSaveDocument extends JsMessage {
@Type(() => IndexedDbDocumentDetails)
details!: IndexedDbDocumentDetails;
version!: string;
}
export class RemoveAutoSaveDocument extends JsMessage {
@@ -386,5 +392,6 @@ export const messageConstructors: Record<string, MessageMaker> = {
DisplayAboutGraphiteDialog,
AutoSaveDocument,
RemoveAutoSaveDocument,
UpdateArtboards,
} as const;
export type JsMessageType = keyof typeof messageConstructors;
+21 -9
View File
@@ -1,9 +1,9 @@
import { AutoSaveDocument, RemoveAutoSaveDocument } from "@/dispatcher/js-messages";
import { DocumentsState } from "@/state/documents";
import { EditorState } from "@/state/wasm-loader";
import { EditorState, getWasmInstance } from "@/state/wasm-loader";
const GRAPHITE_INDEXED_DB_VERSION = 2;
const GRAPHITE_INDEXED_DB_NAME = "graphite-indexed-db";
const GRAPHITE_INDEXED_DB_VERSION = 1;
const GRAPHITE_AUTO_SAVE_STORE = "auto-save-documents";
const GRAPHITE_AUTO_SAVE_ORDER_KEY = "auto-save-documents-order";
@@ -12,9 +12,12 @@ const databaseConnection: Promise<IDBDatabase> = new Promise((resolve) => {
dbOpenRequest.onupgradeneeded = (): void => {
const db = dbOpenRequest.result;
if (!db.objectStoreNames.contains(GRAPHITE_AUTO_SAVE_STORE)) {
db.createObjectStore(GRAPHITE_AUTO_SAVE_STORE, { keyPath: "details.id" });
// Wipes out all auto-save data on upgrade
if (db.objectStoreNames.contains(GRAPHITE_AUTO_SAVE_STORE)) {
db.deleteObjectStore(GRAPHITE_AUTO_SAVE_STORE);
}
db.createObjectStore(GRAPHITE_AUTO_SAVE_STORE, { keyPath: "details.id" });
};
dbOpenRequest.onerror = (): void => {
@@ -41,8 +44,13 @@ export function createAutoSaveManager(editor: EditorState, documents: DocumentsS
const documentOrder: string[] = JSON.parse(window.localStorage.getItem(GRAPHITE_AUTO_SAVE_ORDER_KEY) || "[]");
const orderedSavedDocuments = documentOrder.map((id) => previouslySavedDocuments.find((autoSave) => autoSave.details.id === id)).filter((x) => x !== undefined) as AutoSaveDocument[];
const currentDocumentVersion = getWasmInstance().graphite_version();
orderedSavedDocuments.forEach((doc: AutoSaveDocument) => {
editor.instance.open_auto_saved_document(BigInt(doc.details.id), doc.details.name, doc.details.is_saved, doc.document);
if (doc.version === currentDocumentVersion) {
editor.instance.open_auto_saved_document(BigInt(doc.details.id), doc.details.name, doc.details.is_saved, doc.document);
} else {
removeDocument(doc.details.id);
}
});
resolve(undefined);
};
@@ -55,6 +63,13 @@ export function createAutoSaveManager(editor: EditorState, documents: DocumentsS
window.localStorage.setItem(GRAPHITE_AUTO_SAVE_ORDER_KEY, JSON.stringify(documentOrder));
};
const removeDocument = async (id: string): Promise<void> => {
const db = await databaseConnection;
const transaction = db.transaction(GRAPHITE_AUTO_SAVE_STORE, "readwrite");
transaction.objectStore(GRAPHITE_AUTO_SAVE_STORE).delete(id);
storeDocumentOrder();
};
editor.dispatcher.subscribeJsMessage(AutoSaveDocument, async (autoSaveDocument) => {
const db = await databaseConnection;
const transaction = db.transaction(GRAPHITE_AUTO_SAVE_STORE, "readwrite");
@@ -63,10 +78,7 @@ export function createAutoSaveManager(editor: EditorState, documents: DocumentsS
});
editor.dispatcher.subscribeJsMessage(RemoveAutoSaveDocument, async (removeAutoSaveDocument) => {
const db = await databaseConnection;
const transaction = db.transaction(GRAPHITE_AUTO_SAVE_STORE, "readwrite");
transaction.objectStore(GRAPHITE_AUTO_SAVE_STORE).delete(removeAutoSaveDocument.document_id);
storeDocumentOrder();
removeDocument(removeAutoSaveDocument.document_id);
});
// On creation
+1 -1
View File
@@ -57,7 +57,7 @@ function panicProxy<T extends object>(module: T): T {
return new Proxy<T>(module, proxyHandler);
}
function getWasmInstance(): WasmInstance {
export function getWasmInstance(): WasmInstance {
if (wasmImport) return wasmImport;
throw new Error("Editor WASM backend was not initialized at application startup");
}