Skip to content

Commit

Permalink
store history in the OPFS instead of localStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
k-yle committed Dec 20, 2024
1 parent 28183c7 commit d581853
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 7 deletions.
17 changes: 10 additions & 7 deletions modules/core/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { dispatch as d3_dispatch } from 'd3-dispatch';
import { easeLinear as d3_easeLinear } from 'd3-ease';
import { select as d3_select } from 'd3-selection';

import { prefs } from './preferences';
import { asyncPrefs, prefs } from './preferences';
import { coreDifference } from './difference';
import { coreGraph } from './graph';
import { coreTree } from './tree';
Expand All @@ -19,7 +19,7 @@ export function coreHistory(context) {
var lock = utilSessionMutex('lock');

// restorable if iD not open in another window/tab and a saved history exists in localStorage
var _hasUnresolvedRestorableChanges = lock.lock() && !!prefs(getKey('saved_history'));
var _hasUnresolvedRestorableChanges = lock.lock() && !!prefs(getKey('has_saved_history'));

var duration = 150;
var _imageryUsed = [];
Expand Down Expand Up @@ -659,7 +659,9 @@ export function coreHistory(context) {
if (lock.locked() &&
// don't overwrite existing, unresolved changes
!_hasUnresolvedRestorableChanges) {
const success = prefs(getKey('saved_history'), history.toJSON() || null);
const json = history.toJSON();
prefs(getKey('has_saved_history'), !!json);
const success = asyncPrefs.set(getKey('saved_history'), json || null);

if (!success) dispatch.call('storage_error');
}
Expand All @@ -672,7 +674,8 @@ export function coreHistory(context) {
context.debouncedSave.cancel();
if (lock.locked()) {
_hasUnresolvedRestorableChanges = false;
prefs(getKey('saved_history'), null);
prefs(getKey('has_saved_history'), null);
asyncPrefs.set(getKey('saved_history'), null);

// clear the changeset metadata associated with the saved history
prefs('comment', null);
Expand All @@ -684,7 +687,7 @@ export function coreHistory(context) {


savedHistoryJSON: function() {
return prefs(getKey('saved_history'));
return asyncPrefs.get(getKey('saved_history'));
},


Expand All @@ -694,10 +697,10 @@ export function coreHistory(context) {


// load history from a version stored in localStorage
restore: function() {
restore: async function() {
if (lock.locked()) {
_hasUnresolvedRestorableChanges = false;
var json = this.savedHistoryJSON();
var json = await this.savedHistoryJSON();
if (json) history.fromJSON(json, true);
}
},
Expand Down
43 changes: 43 additions & 0 deletions modules/core/preferences.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

// https://github.com/openstreetmap/iD/issues/772
// http://mathiasbynens.be/notes/localstorage-pattern#comment-9
/** @type {Storage} */
let _storage;
try { _storage = localStorage; } catch {} // eslint-disable-line no-empty
_storage = _storage || (() => {
Expand Down Expand Up @@ -48,4 +49,46 @@ corePreferences.onChange = function(k, handler) {
_listeners[k].push(handler);
};

class AsyncPreferences {
/** @type {Record<string, FileSystemWritableFileStream>} */
writableCache = {};

/** @param {string} key */
async get(key) {
try {
const rootFolder = await navigator.storage.getDirectory();
const fileHandle = await rootFolder.getFileHandle(key, {
create: true,
});
const file = await fileHandle.getFile();
return await file.text();
} catch {
// async storage is not available, so fallback to localStorage
return /** @type {string | null} */ (corePreferences(key));
}
}

/**
* @param {string} key
* @param {string} value
*/
async set(key, value) {
try {
this.writableCache[key] ||= await navigator.storage
.getDirectory()
.then(rootFolder => rootFolder.getFileHandle(key, { create: true }))
.then(fileHandle => fileHandle.createWritable());

await this.writableCache[key].write(new Blob([value]));
return true;
} catch {
// async storage is not available, so fallback to localStorage
return corePreferences(key, value);
}
}
};

export const asyncPrefs = new AsyncPreferences();


export { corePreferences as prefs };

0 comments on commit d581853

Please sign in to comment.