Implement sidebar tree view state persistence
This commit is contained in:
@@ -45,6 +45,12 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var fileTreeExpansionRestoreTimer = 0;
|
||||
var fileTreeCommandBatchRefreshParents = new Map();
|
||||
var FILETREE_EXPANSION_STORAGE_KEY = 'mnote.localFileTree.expandedRelativePaths.v1';
|
||||
var SIDEBAR_TREE_VIEW_STATE_KEY = 'mnote.sidebarTreeViewState.v1:{userId}:{workspaceId}:{sourceKind}:{treeKind}:{rootUriHash}:{scopeHash}';
|
||||
var SIDEBAR_TREE_VIEW_STATE_API_KEY = 'sidebarTreeViewState.v1';
|
||||
var SIDEBAR_TREE_VIEW_STATE_SAVE_DELAY_MS = 500;
|
||||
var sidebarTreeViewStates = new Map();
|
||||
var sidebarTreeViewStateRequests = new Map();
|
||||
var sidebarTreeViewStateSaveTimers = new Map();
|
||||
|
||||
function updateTitleEverywhere(documentId, title) {
|
||||
if (!documentId) return;
|
||||
@@ -438,12 +444,12 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
}
|
||||
|
||||
function refreshLocalFolderAfterCommand(action, result, body) {
|
||||
if (!localCommandNeedsProjectionRefresh(action, result)) return;
|
||||
if (!localCommandNeedsProjectionRefresh(action, result)) return Promise.resolve(false);
|
||||
if (body && body.batchId) {
|
||||
queueFileTreeBatchRefresh(body.batchId, result, body);
|
||||
return;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
void fileTreeRefreshParentsForCommand(result, body);
|
||||
return fileTreeRefreshParentsForCommand(result, body);
|
||||
}
|
||||
|
||||
function sortOrderFromDelta(data) {
|
||||
@@ -624,6 +630,271 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
return fileTreeParentKey(currentRootUri(), parentRelativePath);
|
||||
}
|
||||
|
||||
function stableTreeViewStateHash(value) {
|
||||
var hash = 0xcbf29ce484222325n;
|
||||
var prime = 0x100000001b3n;
|
||||
var text = String(value || '').trim();
|
||||
for (var index = 0; index < text.length; index += 1) {
|
||||
hash ^= BigInt(text.charCodeAt(index) & 0xff);
|
||||
hash = (hash * prime) & 0xffffffffffffffffn;
|
||||
}
|
||||
return hash.toString(16).padStart(16, '0');
|
||||
}
|
||||
|
||||
function currentActorStorageId() {
|
||||
var body = document.body instanceof HTMLElement ? document.body : null;
|
||||
var fromBody = body ? String(body.getAttribute('data-mnote-actor-id') || '').trim() : '';
|
||||
if (fromBody) return fromBody;
|
||||
var match = document.cookie.match(/(?:^|;\s*)mnote_actor_id=([^;]+)/);
|
||||
if (match) {
|
||||
try {
|
||||
return decodeURIComponent(match[1]);
|
||||
} catch (_) {
|
||||
return match[1] || '';
|
||||
}
|
||||
}
|
||||
return 'anonymous';
|
||||
}
|
||||
|
||||
function sidebarTreeViewScope(treeKind) {
|
||||
var normalizedTreeKind = String(treeKind || '').trim() === 'pagetree' ? 'pagetree' : 'filetree';
|
||||
var rootUri = String(currentRootUri() || '').trim();
|
||||
var scope = currentFileTreeScope() || 'root';
|
||||
return {
|
||||
userId: currentActorStorageId(),
|
||||
workspaceId: String(currentWorkspaceId() || '').trim() || (rootUri ? 'local:' + rootUri.replace(/^file:\/\//, '').replace(/[^A-Za-z0-9]+/g, '_') : 'default'),
|
||||
sourceKind: String(currentSourceKind() || 'convex_workspace').trim() || 'convex_workspace',
|
||||
treeKind: normalizedTreeKind,
|
||||
rootUri: rootUri,
|
||||
scope: scope
|
||||
};
|
||||
}
|
||||
|
||||
function sidebarTreeViewStateMapKey(scope) {
|
||||
return [
|
||||
scope.userId,
|
||||
scope.workspaceId,
|
||||
scope.sourceKind,
|
||||
scope.treeKind,
|
||||
scope.rootUri,
|
||||
scope.scope
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function sidebarTreeViewLocalStorageKey(scope) {
|
||||
return SIDEBAR_TREE_VIEW_STATE_KEY
|
||||
.replace('{userId}', encodeURIComponent(scope.userId || 'anonymous'))
|
||||
.replace('{workspaceId}', encodeURIComponent(scope.workspaceId || 'default'))
|
||||
.replace('{sourceKind}', encodeURIComponent(scope.sourceKind || 'convex_workspace'))
|
||||
.replace('{treeKind}', encodeURIComponent(scope.treeKind || 'filetree'))
|
||||
.replace('{rootUriHash}', stableTreeViewStateHash(scope.rootUri || ''))
|
||||
.replace('{scopeHash}', stableTreeViewStateHash(scope.scope || 'root'));
|
||||
}
|
||||
|
||||
function normalizeSidebarTreeViewState(treeKind, value, source) {
|
||||
var scope = sidebarTreeViewScope(treeKind);
|
||||
var state = value && typeof value === 'object' ? value : {};
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
treeKind: scope.treeKind,
|
||||
rootUri: scope.rootUri,
|
||||
scope: scope.scope,
|
||||
expandedIds: new Set(Array.isArray(state.expandedIds) ? state.expandedIds.map(String).filter(Boolean) : []),
|
||||
expandedRelativePaths: new Set(Array.isArray(state.expandedRelativePaths) ? state.expandedRelativePaths.map(normalizeFileTreeRelativePath).filter(Boolean) : []),
|
||||
selectedId: String(state.selectedId || '').trim(),
|
||||
focusedId: String(state.focusedId || '').trim(),
|
||||
activeId: String(state.activeId || '').trim(),
|
||||
scrollTop: Math.max(0, Number(state.scrollTop || 0) || 0),
|
||||
updatedAtMs: Number(state.updatedAtMs || 0) || 0,
|
||||
hasUserState: source === 'sqlite' || source === 'local' || source === 'legacy',
|
||||
source: source || 'default'
|
||||
};
|
||||
}
|
||||
|
||||
function serializeSidebarTreeViewState(state) {
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
treeKind: state.treeKind,
|
||||
rootUri: state.rootUri,
|
||||
scope: state.scope,
|
||||
expandedIds: Array.from(state.expandedIds || []).slice(0, 512),
|
||||
expandedRelativePaths: Array.from(state.expandedRelativePaths || []).slice(0, 512),
|
||||
selectedId: String(state.selectedId || ''),
|
||||
focusedId: String(state.focusedId || ''),
|
||||
activeId: String(state.activeId || ''),
|
||||
scrollTop: Math.max(0, Number(state.scrollTop || 0) || 0),
|
||||
updatedAtMs: Number(state.updatedAtMs || 0) || Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
function sidebarTreeViewStateFor(treeKind) {
|
||||
var scope = sidebarTreeViewScope(treeKind);
|
||||
var key = sidebarTreeViewStateMapKey(scope);
|
||||
var state = sidebarTreeViewStates.get(key);
|
||||
if (!state) {
|
||||
var localValue = null;
|
||||
try {
|
||||
if (window.localStorage) {
|
||||
localValue = JSON.parse(window.localStorage.getItem(sidebarTreeViewLocalStorageKey(scope)) || 'null');
|
||||
}
|
||||
} catch (_) {
|
||||
localValue = null;
|
||||
}
|
||||
state = normalizeSidebarTreeViewState(scope.treeKind, localValue, localValue ? 'local' : 'default');
|
||||
sidebarTreeViewStates.set(key, state);
|
||||
void loadSidebarTreeViewState(scope.treeKind);
|
||||
}
|
||||
if (scope.treeKind === 'filetree') {
|
||||
fileTreeViewState.expandedParents = state.expandedRelativePaths;
|
||||
fileTreeExpandedRelativePaths = state.expandedRelativePaths;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function loadSidebarTreeViewState(treeKind) {
|
||||
var scope = sidebarTreeViewScope(treeKind);
|
||||
var key = sidebarTreeViewStateMapKey(scope);
|
||||
if (sidebarTreeViewStateRequests.has(key)) return sidebarTreeViewStateRequests.get(key);
|
||||
var url = new URL('/api/tree/view-state', window.location.origin);
|
||||
url.searchParams.set('workspaceId', scope.workspaceId);
|
||||
url.searchParams.set('sourceKind', scope.sourceKind);
|
||||
url.searchParams.set('treeKind', scope.treeKind);
|
||||
if (scope.rootUri) url.searchParams.set('rootUri', scope.rootUri);
|
||||
url.searchParams.set('scope', scope.scope);
|
||||
var promise = fetch(url.toString(), {
|
||||
credentials: 'include',
|
||||
cache: 'no-store',
|
||||
headers: { accept: 'application/json' }
|
||||
}).then(function(response) {
|
||||
return response.json().catch(function() { return null; }).then(function(payload) {
|
||||
if (!response.ok) return null;
|
||||
var result = payload && payload.result ? payload.result : null;
|
||||
if (!result || !result.state) return null;
|
||||
var latestScope = sidebarTreeViewScope(scope.treeKind);
|
||||
if (sidebarTreeViewStateMapKey(latestScope) !== key) return null;
|
||||
var loaded = normalizeSidebarTreeViewState(scope.treeKind, result.state, result.source === 'sqlite' ? 'sqlite' : 'default');
|
||||
sidebarTreeViewStates.set(key, loaded);
|
||||
applySidebarTreeViewState(scope.treeKind, loaded);
|
||||
return loaded;
|
||||
});
|
||||
}).catch(function(error) {
|
||||
document.documentElement.setAttribute('data-mnote-sidebar-tree-view-state-load-error', String(error && error.message || error || 'load_failed'));
|
||||
return null;
|
||||
}).finally(function() {
|
||||
sidebarTreeViewStateRequests.delete(key);
|
||||
});
|
||||
sidebarTreeViewStateRequests.set(key, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function persistSidebarTreeViewState(treeKind, options) {
|
||||
options = options || {};
|
||||
var scope = sidebarTreeViewScope(treeKind);
|
||||
var key = sidebarTreeViewStateMapKey(scope);
|
||||
var state = sidebarTreeViewStateFor(scope.treeKind);
|
||||
state.hasUserState = true;
|
||||
state.source = state.source === 'legacy' ? 'legacy' : 'local';
|
||||
state.updatedAtMs = Date.now();
|
||||
var serialized = serializeSidebarTreeViewState(state);
|
||||
try {
|
||||
if (window.localStorage) {
|
||||
window.localStorage.setItem(sidebarTreeViewLocalStorageKey(scope), JSON.stringify(serialized));
|
||||
}
|
||||
} catch (_) {}
|
||||
var flush = function() {
|
||||
sidebarTreeViewStateSaveTimers.delete(key);
|
||||
var latestScope = sidebarTreeViewScope(scope.treeKind);
|
||||
if (sidebarTreeViewStateMapKey(latestScope) !== key) return;
|
||||
fetch('/api/tree/view-state', {
|
||||
method: 'PUT',
|
||||
credentials: 'include',
|
||||
cache: 'no-store',
|
||||
headers: { accept: 'application/json', 'content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
workspaceId: scope.workspaceId,
|
||||
sourceKind: scope.sourceKind,
|
||||
treeKind: scope.treeKind,
|
||||
rootUri: scope.rootUri,
|
||||
scope: scope.scope,
|
||||
state: serialized
|
||||
})
|
||||
}).then(function(response) {
|
||||
return response.json().catch(function() { return null; }).then(function(payload) {
|
||||
if (!response.ok) throw new Error(payload && (payload.error || payload.message) || 'tree_view_state_save_failed_' + response.status);
|
||||
document.documentElement.setAttribute('data-mnote-sidebar-tree-view-state-saved', scope.treeKind + ':' + scope.scope);
|
||||
return payload;
|
||||
});
|
||||
}).catch(function(error) {
|
||||
document.documentElement.setAttribute('data-mnote-sidebar-tree-view-state-save-error', String(error && error.message || error || 'save_failed'));
|
||||
});
|
||||
};
|
||||
if (options.flush) {
|
||||
if (sidebarTreeViewStateSaveTimers.has(key)) {
|
||||
window.clearTimeout(sidebarTreeViewStateSaveTimers.get(key));
|
||||
sidebarTreeViewStateSaveTimers.delete(key);
|
||||
}
|
||||
flush();
|
||||
return;
|
||||
}
|
||||
if (sidebarTreeViewStateSaveTimers.has(key)) window.clearTimeout(sidebarTreeViewStateSaveTimers.get(key));
|
||||
sidebarTreeViewStateSaveTimers.set(key, window.setTimeout(flush, SIDEBAR_TREE_VIEW_STATE_SAVE_DELAY_MS));
|
||||
}
|
||||
|
||||
function flushPendingSidebarTreeViewState(treeKind) {
|
||||
var scope = sidebarTreeViewScope(treeKind);
|
||||
var key = sidebarTreeViewStateMapKey(scope);
|
||||
if (!sidebarTreeViewStateSaveTimers.has(key)) return;
|
||||
persistSidebarTreeViewState(scope.treeKind, { flush: true });
|
||||
}
|
||||
|
||||
function applySidebarTreeViewState(treeKind, state) {
|
||||
if (String(treeKind || '').trim() === 'pagetree') {
|
||||
applyPageTreeExpansionState(state);
|
||||
return;
|
||||
}
|
||||
fileTreeExpandedRelativePaths = state.expandedRelativePaths;
|
||||
fileTreeViewState.expandedParents = state.expandedRelativePaths;
|
||||
restorePersistedFileTreeExpansionState();
|
||||
}
|
||||
|
||||
function applyPageTreeExpansionState(state) {
|
||||
if (!state || !state.hasUserState) return false;
|
||||
var changed = false;
|
||||
document.querySelectorAll('#sidebar-tree-root .tree-row[data-shell-mode="page"]').forEach(function(row) {
|
||||
if (!(row instanceof HTMLElement)) return;
|
||||
var nodeId = String(row.getAttribute('data-node-id') || '').trim();
|
||||
var button = row.querySelector('[data-rust-action="toggle"]');
|
||||
var node = row.closest('.tree-node');
|
||||
var children = node ? node.querySelector(':scope > .tree-children') : null;
|
||||
if (!nodeId || !button || !(children instanceof HTMLElement)) return;
|
||||
var expanded = state.expandedIds.has(nodeId);
|
||||
row.setAttribute('aria-expanded', String(expanded));
|
||||
button.setAttribute('aria-expanded', String(expanded));
|
||||
children.classList.toggle('tree-children--collapsed', !expanded);
|
||||
changed = true;
|
||||
});
|
||||
if (changed) document.documentElement.setAttribute('data-mnote-page-tree-view-state-applied', state.scope || 'root');
|
||||
return changed;
|
||||
}
|
||||
|
||||
function pageTreeActiveParentIds(grouped, activeId) {
|
||||
var parentsById = new Map();
|
||||
grouped.forEach(function(items) {
|
||||
items.forEach(function(item) {
|
||||
parentsById.set(nodeIdOf(item), parentIdOf(item));
|
||||
});
|
||||
});
|
||||
var parents = new Set();
|
||||
var cursor = String(activeId || '').trim();
|
||||
var guard = 0;
|
||||
while (cursor && parentsById.has(cursor) && guard < 512) {
|
||||
cursor = String(parentsById.get(cursor) || '').trim();
|
||||
if (cursor) parents.add(cursor);
|
||||
guard += 1;
|
||||
}
|
||||
return parents;
|
||||
}
|
||||
|
||||
function projectionParentRelativePath(projection) {
|
||||
var resolved = readProjection(projection);
|
||||
return String(resolved && (resolved.parentRelativePath || resolved.parent_relative_path) || '').trim();
|
||||
@@ -692,6 +963,9 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
|
||||
function renderPageRows(parentId, grouped, activeId, inheritedDepth) {
|
||||
var computedDepth = Number(inheritedDepth || 0);
|
||||
var pageTreeStateView = sidebarTreeViewStateFor('pagetree');
|
||||
var pageTreeExpandedIds = pageTreeStateView.expandedIds;
|
||||
var activeParentIds = pageTreeActiveParentIds(grouped, activeId);
|
||||
return (grouped.get(parentId) || []).map(function(item) {
|
||||
var nodeId = nodeIdOf(item);
|
||||
var title = titleOf(item);
|
||||
@@ -699,7 +973,10 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var parent = parentIdOf(item);
|
||||
var children = grouped.get(nodeId) || [];
|
||||
var expandable = Boolean(item.expandable || item.childCount > 0 || children.length);
|
||||
var expanded = expandable && item.expandedByDefault !== false;
|
||||
var expanded = expandable && (
|
||||
pageTreeExpandedIds.has(nodeId) ||
|
||||
(!pageTreeStateView.hasUserState && (activeParentIds.has(nodeId) || item.expandedByDefault !== false))
|
||||
);
|
||||
var meta = item && item.resourceMeta && typeof item.resourceMeta === 'object' ? item.resourceMeta : {};
|
||||
var openable = Boolean(meta.documentId || item.documentId || !String(nodeId).startsWith('local-dir:'));
|
||||
var toggle = expandable
|
||||
@@ -816,6 +1093,8 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
}
|
||||
|
||||
function renderFileRows(parentId, grouped, activeId, activeRowId) {
|
||||
var fileTreeStateView = sidebarTreeViewStateFor('filetree');
|
||||
var expandedRelativePaths = fileTreeStateView.expandedRelativePaths;
|
||||
return (grouped.get(parentId) || []).map(function(item) {
|
||||
var nodeId = nodeIdOf(item);
|
||||
var rowId = rowIdOf(item);
|
||||
@@ -835,7 +1114,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
: normalizeMindmapFileTreeTitle(rawTitle, assetId, iconKind, objectIdentity);
|
||||
var children = grouped.get(nodeId) || [];
|
||||
var expandable = Boolean(item.expandable || item.childCount > 0 || children.length || cachedChildren.length);
|
||||
var expanded = expandable && (fileTreeExpandedRelativePaths.has(relativePath) || item.expandedByDefault !== false);
|
||||
var expanded = expandable && (expandedRelativePaths.has(relativePath) || (!fileTreeStateView.hasUserState && item.expandedByDefault !== false));
|
||||
var selected = activeRowId ? rowId === activeRowId : rowId === 'doc:' + activeId;
|
||||
var testId = rowKind === 'document' ? 'filetree-doc-row' : rowKind === 'index' ? 'filetree-index-row' : 'filetree-asset-row';
|
||||
var toggle = expandable
|
||||
@@ -871,7 +1150,9 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
fileTreeState.revisionByParent.clear();
|
||||
fileTreeState.latestGenerationByParent.clear();
|
||||
fileTreeState.requestGeneration += 1;
|
||||
fileTreeExpandedRelativePaths.clear();
|
||||
var nextState = sidebarTreeViewStateFor('filetree');
|
||||
fileTreeExpandedRelativePaths = nextState.expandedRelativePaths;
|
||||
fileTreeViewState.expandedParents = nextState.expandedRelativePaths;
|
||||
loadStoredFileTreeExpansionState(rootUri);
|
||||
}
|
||||
|
||||
@@ -889,21 +1170,31 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var normalizedRootUri = String(rootUri || '').trim();
|
||||
if (!normalizedRootUri || fileTreeExpansionStorageRootUri === normalizedRootUri) return;
|
||||
fileTreeExpansionStorageRootUri = normalizedRootUri;
|
||||
var state = sidebarTreeViewStateFor('filetree');
|
||||
if (state.hasUserState && state.expandedRelativePaths.size) return;
|
||||
var buckets = storedFileTreeExpansionBuckets();
|
||||
var paths = Array.isArray(buckets[normalizedRootUri]) ? buckets[normalizedRootUri] : [];
|
||||
paths.forEach(function(path) {
|
||||
var normalized = String(path || '').trim();
|
||||
if (normalized) fileTreeExpandedRelativePaths.add(normalized);
|
||||
if (normalized) state.expandedRelativePaths.add(normalized);
|
||||
});
|
||||
if (state.expandedRelativePaths.size) {
|
||||
state.hasUserState = true;
|
||||
state.source = 'legacy';
|
||||
fileTreeExpandedRelativePaths = state.expandedRelativePaths;
|
||||
fileTreeViewState.expandedParents = state.expandedRelativePaths;
|
||||
persistSidebarTreeViewState('filetree');
|
||||
}
|
||||
}
|
||||
|
||||
function persistFileTreeExpansionState() {
|
||||
var rootUri = String(currentRootUri() || fileTreeLazyCacheRootUri || '').trim();
|
||||
if (!rootUri) return;
|
||||
var state = sidebarTreeViewStateFor('filetree');
|
||||
try {
|
||||
if (!window.sessionStorage) return;
|
||||
var buckets = storedFileTreeExpansionBuckets();
|
||||
buckets[rootUri] = Array.from(fileTreeExpandedRelativePaths)
|
||||
buckets[rootUri] = Array.from(state.expandedRelativePaths)
|
||||
.map(function(path) { return String(path || '').trim(); })
|
||||
.filter(Boolean)
|
||||
.slice(0, 256);
|
||||
@@ -1144,6 +1435,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
sidebarUrl.searchParams.set('sourceKind', 'local_folder');
|
||||
sidebarUrl.searchParams.set('rootUri', rootUri);
|
||||
if (currentId) sidebarUrl.searchParams.set('rootNodeId', currentId);
|
||||
if (fileTreeScope) sidebarUrl.searchParams.set('parentRelativePath', fileTreeScope);
|
||||
var responses = await Promise.allSettled([
|
||||
fetch(sidebarUrl.toString(), { headers: { accept: 'application/json' } }),
|
||||
refreshFileTreeParent(fileTreeScope)
|
||||
@@ -1281,15 +1573,27 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
|
||||
function setTreeRowExpanded(row, button, expanded) {
|
||||
row.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
||||
var shellMode = row.getAttribute('data-shell-mode') || '';
|
||||
if (button) {
|
||||
button.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
||||
button.textContent = expanded ? '▾' : '▸';
|
||||
if (shellMode === 'filetree') button.textContent = expanded ? '▾' : '▸';
|
||||
}
|
||||
if (shellMode === 'page') {
|
||||
var nodeId = String(row.getAttribute('data-node-id') || '').trim();
|
||||
if (!nodeId) return;
|
||||
var pageState = sidebarTreeViewStateFor('pagetree');
|
||||
if (expanded) pageState.expandedIds.add(nodeId);
|
||||
else pageState.expandedIds.delete(nodeId);
|
||||
pageState.hasUserState = true;
|
||||
persistSidebarTreeViewState('pagetree');
|
||||
return;
|
||||
}
|
||||
var relativePath = localFileTreeRelativePathFromRow(row);
|
||||
if (!relativePath) return;
|
||||
if (expanded) fileTreeExpandedRelativePaths.add(relativePath);
|
||||
else fileTreeExpandedRelativePaths.delete(relativePath);
|
||||
persistFileTreeExpansionState();
|
||||
persistSidebarTreeViewState('filetree');
|
||||
}
|
||||
|
||||
function markExistingFileTreeChildrenLoaded(row, button) {
|
||||
@@ -1462,7 +1766,9 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
restored = true;
|
||||
return;
|
||||
}
|
||||
void loadFileTreeChildren(row, button);
|
||||
void loadFileTreeChildren(row, button).then(function(loaded) {
|
||||
if (loaded) restorePersistedFileTreeExpansionState();
|
||||
});
|
||||
restored = true;
|
||||
});
|
||||
if (restored) document.documentElement.setAttribute('data-mnote-filetree-expansion-restored', 'true');
|
||||
@@ -1471,6 +1777,17 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
|
||||
|
||||
function installTreeLiveApplyEventListeners() {
|
||||
sidebarTreeViewStateFor('pagetree');
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (!document.hidden) return;
|
||||
flushPendingSidebarTreeViewState('filetree');
|
||||
flushPendingSidebarTreeViewState('pagetree');
|
||||
});
|
||||
window.addEventListener('pagehide', function() {
|
||||
flushPendingSidebarTreeViewState('filetree');
|
||||
flushPendingSidebarTreeViewState('pagetree');
|
||||
});
|
||||
|
||||
window.addEventListener('tree:title-updated', function(event) {
|
||||
var detail = event.detail || {};
|
||||
var previousDocumentId = detail.previousDocumentId || (detail.payload && detail.payload.result && detail.payload.result.previousDocumentId) || '';
|
||||
@@ -1611,6 +1928,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
normalizeFileTreePageRenameTitle,
|
||||
objectIdentityAttr,
|
||||
refreshLocalFolderSidebarSnapshot,
|
||||
refreshLocalFolderAfterCommand,
|
||||
removeDocumentRowForMode,
|
||||
renderSidebarSnapshot,
|
||||
revealFileTreeResource,
|
||||
|
||||
Reference in New Issue
Block a user