Refactor: extract widget cache from Client class
This commit is contained in:
+5
-47
@@ -60,8 +60,8 @@ import { DataStore } from "./data/datastore.ts";
|
||||
import { IndexedDBKvPrimitives } from "./data/indexeddb_kv_primitives.ts";
|
||||
import { DataStoreMQ } from "./data/mq.datastore.ts";
|
||||
|
||||
import { LimitedMap } from "@silverbulletmd/silverbullet/lib/limited_map";
|
||||
import { fsEndpoint } from "./spaces/constants.ts";
|
||||
import { WidgetCache } from "./widget_cache.ts";
|
||||
import { diffAndPrepareChanges } from "./codemirror/cm_util.ts";
|
||||
import { DocumentEditor } from "./document_editor.ts";
|
||||
import { parseExpressionString } from "./space_lua/parse.ts";
|
||||
@@ -97,12 +97,6 @@ declare global {
|
||||
var client: Client;
|
||||
}
|
||||
|
||||
type WidgetCacheItem = {
|
||||
html: string;
|
||||
block?: boolean;
|
||||
copyContent?: string;
|
||||
};
|
||||
|
||||
// TODO: Clean this up, this has become a god class...
|
||||
export class Client {
|
||||
// Event bus used to communicate between components
|
||||
@@ -149,18 +143,7 @@ export class Client {
|
||||
// Progress circle handling
|
||||
private progressTimeout?: ReturnType<typeof setTimeout>;
|
||||
// Widget and image height caching
|
||||
private widgetCache = new LimitedMap<WidgetCacheItem>(100); // bodyText -> WidgetCacheItem
|
||||
debouncedWidgetCacheFlush = throttle(() => {
|
||||
this.ds
|
||||
.set(["cache", "widgets"], this.widgetCache.toJSON())
|
||||
.catch(console.error);
|
||||
}, 2000);
|
||||
private widgetHeightCache = new LimitedMap<number>(1000); // bodytext -> height
|
||||
debouncedWidgetHeightCacheFlush = throttle(() => {
|
||||
this.ds
|
||||
.set(["cache", "widgetHeight"], this.widgetHeightCache.toJSON())
|
||||
.catch(console.error);
|
||||
}, 2000);
|
||||
widgetCache!: WidgetCache;
|
||||
objectIndex!: ObjectIndex;
|
||||
|
||||
constructor(
|
||||
@@ -205,6 +188,8 @@ export class Client {
|
||||
// Setup message queue on top of that
|
||||
this.mq = new DataStoreMQ(this.ds, this.eventHook);
|
||||
|
||||
this.widgetCache = new WidgetCache(this.ds);
|
||||
|
||||
this.objectIndex = new ObjectIndex(
|
||||
this.ds,
|
||||
this.config,
|
||||
@@ -255,7 +240,7 @@ export class Client {
|
||||
}
|
||||
}
|
||||
|
||||
await this.loadCaches();
|
||||
await this.widgetCache.load();
|
||||
|
||||
// Let's ping the remote space to ensure we're authenticated properly, if not will result in a redirect to auth page
|
||||
try {
|
||||
@@ -1312,33 +1297,6 @@ export class Client {
|
||||
return;
|
||||
}
|
||||
|
||||
async loadCaches() {
|
||||
const [widgetHeightCache, widgetCache] = await this.ds.batchGet([
|
||||
["cache", "widgetHeight"],
|
||||
["cache", "widgets"],
|
||||
]);
|
||||
this.widgetHeightCache = new LimitedMap(1000, widgetHeightCache || {});
|
||||
this.widgetCache = new LimitedMap(100, widgetCache || {});
|
||||
}
|
||||
|
||||
setCachedWidgetHeight(bodyText: string, height: number) {
|
||||
this.widgetHeightCache.set(bodyText, height);
|
||||
this.debouncedWidgetHeightCacheFlush();
|
||||
}
|
||||
|
||||
getCachedWidgetHeight(bodyText: string): number {
|
||||
return this.widgetHeightCache.get(bodyText) ?? -1;
|
||||
}
|
||||
|
||||
setWidgetCache(key: string, cacheItem: WidgetCacheItem) {
|
||||
this.widgetCache.set(key, cacheItem);
|
||||
this.debouncedWidgetCacheFlush();
|
||||
}
|
||||
|
||||
getWidgetCache(key: string): WidgetCacheItem | undefined {
|
||||
return this.widgetCache.get(key);
|
||||
}
|
||||
|
||||
async handleServiceWorkerMessage(message: ServiceWorkerSourceMessage) {
|
||||
switch (message.type) {
|
||||
case "space-sync-complete": {
|
||||
|
||||
@@ -20,7 +20,7 @@ export class IFrameWidget extends WidgetType {
|
||||
}
|
||||
|
||||
override get estimatedHeight(): number {
|
||||
const cachedHeight = this.client.getCachedWidgetHeight(this.bodyText);
|
||||
const cachedHeight = this.client.widgetCache.getCachedWidgetHeight(this.bodyText);
|
||||
// console.log("Calling estimated height", this.bodyText, cachedHeight);
|
||||
return cachedHeight > 0 ? cachedHeight : 150;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export class LuaWidget extends WidgetType {
|
||||
}
|
||||
|
||||
override get estimatedHeight(): number {
|
||||
return this.client.getCachedWidgetHeight(this.cacheKey);
|
||||
return this.client.widgetCache.getCachedWidgetHeight(this.cacheKey);
|
||||
}
|
||||
|
||||
toDOM(): HTMLElement {
|
||||
@@ -76,7 +76,7 @@ export class LuaWidget extends WidgetType {
|
||||
wrapperSpan.className = "sb-lua-wrapper";
|
||||
const innerDiv = document.createElement("div");
|
||||
wrapperSpan.appendChild(innerDiv);
|
||||
const cacheItem = this.client.getWidgetCache(this.cacheKey);
|
||||
const cacheItem = this.client.widgetCache.getWidgetCache(this.cacheKey);
|
||||
if (cacheItem) {
|
||||
if (cacheItem.block) {
|
||||
innerDiv.className += " sb-lua-directive-block";
|
||||
@@ -126,8 +126,8 @@ export class LuaWidget extends WidgetType {
|
||||
if (widgetContent === null || widgetContent === undefined) {
|
||||
if (!this.renderEmpty) {
|
||||
div.innerHTML = "";
|
||||
this.client.setWidgetCache(this.cacheKey, { html: "", block: false });
|
||||
this.client.setCachedWidgetHeight(this.cacheKey, div.clientHeight);
|
||||
this.client.widgetCache.setWidgetCache(this.cacheKey, { html: "", block: false });
|
||||
this.client.widgetCache.setCachedWidgetHeight(this.cacheKey, div.clientHeight);
|
||||
return;
|
||||
}
|
||||
widgetContent = { markdown: "nil", _isWidget: true };
|
||||
@@ -195,8 +195,8 @@ export class LuaWidget extends WidgetType {
|
||||
if (!trimmedMarkdown) {
|
||||
// Net empty result after expansion
|
||||
div.innerHTML = "";
|
||||
this.client.setWidgetCache(this.cacheKey, { html: "", block: false });
|
||||
this.client.setCachedWidgetHeight(this.cacheKey, div.clientHeight);
|
||||
this.client.widgetCache.setWidgetCache(this.cacheKey, { html: "", block: false });
|
||||
this.client.widgetCache.setCachedWidgetHeight(this.cacheKey, div.clientHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -245,12 +245,12 @@ export class LuaWidget extends WidgetType {
|
||||
|
||||
// Let's give it a tick, then measure and cache
|
||||
setTimeout(() => {
|
||||
this.client.setWidgetCache(this.cacheKey, {
|
||||
this.client.widgetCache.setWidgetCache(this.cacheKey, {
|
||||
html: html?.outerHTML || "",
|
||||
block,
|
||||
copyContent: copyContent,
|
||||
});
|
||||
this.client.setCachedWidgetHeight(this.cacheKey, div.offsetHeight);
|
||||
this.client.widgetCache.setCachedWidgetHeight(this.cacheKey, div.offsetHeight);
|
||||
// Because of the rejiggering of the DOM, we need to do a no-op cursor move to make sure it's positioned correctly
|
||||
this.client.editorView.dispatch({
|
||||
selection: this.client.editorView.state.selection,
|
||||
|
||||
@@ -34,7 +34,7 @@ class TableViewWidget extends WidgetType {
|
||||
}
|
||||
|
||||
override get estimatedHeight(): number {
|
||||
return this.client.getCachedWidgetHeight(`table:${this.tableBodyText}`);
|
||||
return this.client.widgetCache.getCachedWidgetHeight(`table:${this.tableBodyText}`);
|
||||
}
|
||||
|
||||
toDOM(): HTMLElement {
|
||||
@@ -78,7 +78,7 @@ class TableViewWidget extends WidgetType {
|
||||
// Give it a tick to render
|
||||
attachWidgetEventHandlers(dom, this.client, this.tableBodyText);
|
||||
|
||||
this.client.setCachedWidgetHeight(
|
||||
this.client.widgetCache.setCachedWidgetHeight(
|
||||
`table:${this.tableBodyText}`,
|
||||
dom.clientHeight,
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ class ArrayWidget extends WidgetType {
|
||||
}
|
||||
|
||||
override get estimatedHeight(): number {
|
||||
return this.client.getCachedWidgetHeight(this.cacheKey);
|
||||
return this.client.widgetCache.getCachedWidgetHeight(this.cacheKey);
|
||||
}
|
||||
|
||||
toDOM(): HTMLElement {
|
||||
@@ -28,7 +28,7 @@ class ArrayWidget extends WidgetType {
|
||||
div.className = "sb-widget-array";
|
||||
|
||||
// This doesn't do that much, but it also doesn't really hurt
|
||||
const cacheItem = this.client.getWidgetCache(this.cacheKey);
|
||||
const cacheItem = this.client.widgetCache.getWidgetCache(this.cacheKey);
|
||||
if (cacheItem) {
|
||||
div.innerHTML = cacheItem.html;
|
||||
}
|
||||
@@ -91,11 +91,11 @@ class ArrayWidget extends WidgetType {
|
||||
|
||||
// Wait for the clientHeight to settle
|
||||
setTimeout(() => {
|
||||
this.client.setWidgetCache(this.cacheKey, {
|
||||
this.client.widgetCache.setWidgetCache(this.cacheKey, {
|
||||
block: true,
|
||||
html: div.innerHTML,
|
||||
});
|
||||
this.client.setCachedWidgetHeight(this.cacheKey, div.clientHeight);
|
||||
this.client.widgetCache.setCachedWidgetHeight(this.cacheKey, div.clientHeight);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ export function mountIFrame(
|
||||
// iframe.height = data.height + "px";
|
||||
iframe.style.height = `${data.height}px`;
|
||||
if (widgetHeightCacheKey) {
|
||||
client.setCachedWidgetHeight(widgetHeightCacheKey, data.height);
|
||||
client.widgetCache.setCachedWidgetHeight(widgetHeightCacheKey, data.height);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -192,7 +192,7 @@ export function mountIFrame(
|
||||
if (resolvedContent.height) {
|
||||
iframe.style.height = `${resolvedContent.height}px`;
|
||||
if (widgetHeightCacheKey) {
|
||||
client.setCachedWidgetHeight(
|
||||
client.widgetCache.setCachedWidgetHeight(
|
||||
widgetHeightCacheKey!,
|
||||
resolvedContent.height,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { throttle } from "@silverbulletmd/silverbullet/lib/async";
|
||||
import { LimitedMap } from "@silverbulletmd/silverbullet/lib/limited_map";
|
||||
import type { DataStore } from "./data/datastore.ts";
|
||||
|
||||
export type WidgetCacheItem = {
|
||||
html: string;
|
||||
block?: boolean;
|
||||
copyContent?: string;
|
||||
};
|
||||
|
||||
export class WidgetCache {
|
||||
private widgetCache = new LimitedMap<WidgetCacheItem>(100);
|
||||
private widgetHeightCache = new LimitedMap<number>(1000);
|
||||
|
||||
private debouncedWidgetCacheFlush = throttle(() => {
|
||||
this.ds
|
||||
.set(["cache", "widgets"], this.widgetCache.toJSON())
|
||||
.catch(console.error);
|
||||
}, 2000);
|
||||
|
||||
private debouncedWidgetHeightCacheFlush = throttle(() => {
|
||||
this.ds
|
||||
.set(["cache", "widgetHeight"], this.widgetHeightCache.toJSON())
|
||||
.catch(console.error);
|
||||
}, 2000);
|
||||
|
||||
constructor(private ds: DataStore) {}
|
||||
|
||||
async load() {
|
||||
const [widgetHeightCache, widgetCache] = await this.ds.batchGet([
|
||||
["cache", "widgetHeight"],
|
||||
["cache", "widgets"],
|
||||
]);
|
||||
this.widgetHeightCache = new LimitedMap(1000, widgetHeightCache || {});
|
||||
this.widgetCache = new LimitedMap(100, widgetCache || {});
|
||||
}
|
||||
|
||||
setCachedWidgetHeight(bodyText: string, height: number) {
|
||||
this.widgetHeightCache.set(bodyText, height);
|
||||
this.debouncedWidgetHeightCacheFlush();
|
||||
}
|
||||
|
||||
getCachedWidgetHeight(bodyText: string): number {
|
||||
return this.widgetHeightCache.get(bodyText) ?? -1;
|
||||
}
|
||||
|
||||
setWidgetCache(key: string, cacheItem: WidgetCacheItem) {
|
||||
this.widgetCache.set(key, cacheItem);
|
||||
this.debouncedWidgetCacheFlush();
|
||||
}
|
||||
|
||||
getWidgetCache(key: string): WidgetCacheItem | undefined {
|
||||
return this.widgetCache.get(key);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user