-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathSnapshotStorage.js
More file actions
171 lines (140 loc) · 4.7 KB
/
SnapshotStorage.js
File metadata and controls
171 lines (140 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright (c) 2026 Music Blocks Contributors
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the The GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// You should have received a copy of the GNU Affero General Public
// License along with this library; if not, write to the Free Software
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
/*
global localforage
*/
/*
exported SnapshotStorage
*/
/**
* Minimal storage helper for project snapshots.
* Stores snapshots in IndexedDB using localforage.
*
* This is a read-only history feature - no restore, delete, or cleanup.
*/
class SnapshotStorage {
constructor() {
this.LocalStorage = null;
this.LocalStorageKey = "SnapshotHistory";
this.data = null;
// eslint-disable-next-line no-unused-vars
this.dataLoaded = new Promise((resolve, reject) => {
this.fireDataLoaded = resolve;
});
}
/**
* Generate a unique ID for a snapshot
* @returns {string} Unique snapshot ID
*/
generateID() {
const n = Date.now();
const prefix = n.toString();
let suffix = "";
for (let i = 0; i < 3; i++) suffix += Math.floor(Math.random() * 10).toString();
return prefix + suffix;
}
/**
* Save a snapshot of the current project
* @param {string} projectId - Current project ID
* @param {string} projectData - Serialized project data from prepareExport()
* @param {string} description - Optional user description
* @returns {Promise<string>} The snapshot ID
*/
async saveSnapshot(projectId, projectData, description) {
await this.dataLoaded;
const snapshotId = this.generateID();
const snapshot = {
id: snapshotId,
timestamp: Date.now(),
projectId: projectId,
description: description || "",
projectData: projectData
};
// Initialize snapshots array if needed
if (!this.data.snapshots) {
this.data.snapshots = [];
}
this.data.snapshots.push(snapshot);
await this.save();
return snapshotId;
}
/**
* Get all snapshots for a specific project
* @param {string} projectId - Project ID to filter by
* @returns {Promise<Array>} Array of snapshots for the project
*/
async getSnapshots(projectId) {
await this.dataLoaded;
if (!this.data.snapshots) {
return [];
}
return this.data.snapshots.filter(snapshot => snapshot.projectId === projectId);
}
/**
* Get a specific snapshot by ID
* @param {string} snapshotId - Snapshot ID
* @returns {Promise<Object|null>} Snapshot object or null if not found
*/
async getSnapshot(snapshotId) {
await this.dataLoaded;
if (!this.data.snapshots) {
return null;
}
return this.data.snapshots.find(snapshot => snapshot.id === snapshotId) || null;
}
// Internal storage methods
async set(key, obj) {
const jsonobj = JSON.stringify(obj);
await this.LocalStorage.setItem(key, jsonobj);
const savedjsonobj = await this.LocalStorage.getItem(key);
if (savedjsonobj == null) throw new Error("Failed to save snapshot data");
}
async get(key) {
const jsonobj = await this.LocalStorage.getItem(key);
if (jsonobj === null || jsonobj === "") return null;
try {
return JSON.parse(jsonobj);
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
return null;
}
}
async save() {
await this.set(this.LocalStorageKey, this.data);
}
async restore() {
const currentData = await this.get(this.LocalStorageKey);
try {
this.data = typeof currentData === "string" ? JSON.parse(currentData) : currentData;
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
return null;
}
}
async initialiseStorage() {
if (this.data === null || this.data === undefined) {
this.data = {};
}
if (this.data.snapshots === null || this.data.snapshots === undefined) {
this.data.snapshots = [];
}
this.fireDataLoaded();
await this.save();
}
async init() {
// Use IndexedDB via localforage (same as ProjectStorage)
this.LocalStorage = localforage;
await this.restore();
await this.initialiseStorage();
}
}