Giant refactor

This commit is contained in:
Zef Hemel
2022-03-14 18:06:28 +01:00
parent 1984d8eefe
commit 9d41c9e3d6
22 changed files with 3268 additions and 435 deletions
+3 -4
View File
@@ -1,5 +1,5 @@
{
"name": "noot",
"name": "silverbullet",
"version": "1.0.0",
"source": [
"src/index.html"
@@ -7,10 +7,9 @@
"license": "MIT",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"start": "parcel",
"start": "parcel serve --no-cache",
"build": "parcel build",
"clean": "rm -rf dist",
"check-watch": "tsc --noEmit --watch"
"clean": "rm -rf dist"
},
"devDependencies": {
"@parcel/packager-raw-url": "2.3.2",
+8 -5
View File
@@ -1,14 +1,11 @@
import { Editor } from "./editor";
import { RealtimeSpace } from "./space";
import { Space } from "./space";
import { safeRun } from "./util";
import { io } from "socket.io-client";
let socket = io(`http://${location.hostname}:3000`);
let editor = new Editor(
new RealtimeSpace(socket),
document.getElementById("root")!
);
let editor = new Editor(new Space(socket), document.getElementById("root")!);
safeRun(async () => {
await editor.init();
@@ -16,3 +13,9 @@ safeRun(async () => {
// @ts-ignore
window.editor = editor;
navigator.serviceWorker
.register(new URL("service_worker.ts", import.meta.url), { type: "module" })
.then((r) => {
console.log("Service worker registered", r);
});
+5 -7
View File
@@ -46,7 +46,7 @@ import { IPageNavigator, PathPageNavigator } from "./navigator";
import customMarkDown from "./parser";
import reducer from "./reducer";
import { smartQuoteKeymap } from "./smart_quotes";
import { RealtimeSpace } from "./space";
import { Space } from "./space";
import customMarkdownStyle from "./style";
import dbSyscalls from "./syscalls/db.localstorage";
import editorSyscalls from "./syscalls/editor.browser";
@@ -77,7 +77,7 @@ export class Editor implements AppEventDispatcher {
viewState: AppViewState;
viewDispatch: React.Dispatch<Action>;
openPages: Map<string, PageState>;
space: RealtimeSpace;
space: Space;
editorCommands: Map<string, AppCommand>;
plugs: Plug<NuggetHook>[];
indexer: Indexer;
@@ -85,7 +85,7 @@ export class Editor implements AppEventDispatcher {
pageNavigator: IPageNavigator;
indexCurrentPageDebounced: () => any;
constructor(space: RealtimeSpace, parent: Element) {
constructor(space: Space, parent: Element) {
this.editorCommands = new Map();
this.openPages = new Map();
this.plugs = [];
@@ -101,7 +101,7 @@ export class Editor implements AppEventDispatcher {
parent: document.getElementById("editor")!,
});
this.pageNavigator = new PathPageNavigator();
this.indexer = new Indexer("page-index", space);
this.indexer = new Indexer(space);
this.indexCurrentPageDebounced = throttle(
this.indexCurrentPage.bind(this),
@@ -175,7 +175,7 @@ export class Editor implements AppEventDispatcher {
dbSyscalls,
editorSyscalls(this),
spaceSyscalls(this),
indexerSyscalls(this.indexer)
indexerSyscalls(this.space)
);
console.log("Now loading core plug");
@@ -319,8 +319,6 @@ export class Editor implements AppEventDispatcher {
key: "Ctrl-.",
mac: "Cmd-.",
run: (target): boolean => {
console.log("YO");
this.viewDispatch({
type: "show-palette",
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 66 KiB

+1 -1
View File
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Page</title>
<title>Silver Bullet</title>
<link rel="stylesheet" href="styles/main.scss" />
<script type="module" src="boot.ts"></script>
<link rel="manifest" href="manifest.json" />
+3 -116
View File
@@ -1,61 +1,13 @@
import { Dexie, Table } from "dexie";
import { AppEventDispatcher, IndexEvent } from "./app_event";
import { Space } from "./space";
import { PageMeta } from "./types";
function constructKey(pageName: string, key: string): string {
return `${pageName}:${key}`;
}
function cleanKey(pageName: string, fromKey: string): string {
return fromKey.substring(pageName.length + 1);
}
export type KV = {
key: string;
value: any;
};
export class Indexer {
db: Dexie;
pageIndex: Table;
space: Space;
constructor(name: string, space: Space) {
this.db = new Dexie(name);
constructor(space: Space) {
this.space = space;
this.db.version(1).stores({
pageIndex: "ck, page, key",
});
this.pageIndex = this.db.table("pageIndex");
}
async clearPageIndexForPage(pageName: string) {
await this.pageIndex.where({ page: pageName }).delete();
}
async clearPageIndex() {
await this.pageIndex.clear();
}
// async setPageIndexPageMeta(pageName: string, meta: PageMeta) {
// await this.set(pageName, "$meta", {
// lastModified: meta.lastModified.getTime(),
// });
// }
// async getPageIndexPageMeta(pageName: string): Promise<PageMeta | null> {
// let meta = await this.get(pageName, "$meta");
// if (meta) {
// return {
// name: pageName,
// lastModified: new Date(meta.lastModified),
// };
// } else {
// return null;
// }
// }
async indexPage(
appEventDispatcher: AppEventDispatcher,
pageName: string,
@@ -63,7 +15,7 @@ export class Indexer {
withFlush: boolean
) {
if (withFlush) {
await this.clearPageIndexForPage(pageName);
await this.space.indexDeletePrefixForPage(pageName, "");
}
let indexEvent: IndexEvent = {
name: pageName,
@@ -74,77 +26,12 @@ export class Indexer {
}
async reindexSpace(space: Space, appEventDispatcher: AppEventDispatcher) {
await this.clearPageIndex();
let allPages = await space.listPages();
// TODO: Parallelize?
for (let page of allPages) {
await space.indexDeletePrefixForPage(page.name, "");
let pageData = await space.readPage(page.name);
await this.indexPage(appEventDispatcher, page.name, pageData.text, false);
}
}
async set(pageName: string, key: string, value: any) {
await this.pageIndex.put({
ck: constructKey(pageName, key),
page: pageName,
key: key,
value: value,
});
}
async batchSet(pageName: string, kvs: KV[]) {
await this.pageIndex.bulkPut(
kvs.map(({ key, value }) => ({
ck: constructKey(pageName, key),
key: key,
page: pageName,
value: value,
}))
);
}
async get(pageName: string, key: string): Promise<any | null> {
let result = await this.pageIndex.get({
ck: constructKey(pageName, key),
});
return result ? result.value : null;
}
async scanPrefixForPage(
pageName: string,
keyPrefix: string
): Promise<{ key: string; value: any }[]> {
let results = await this.pageIndex
.where("ck")
.startsWith(constructKey(pageName, keyPrefix))
.toArray();
return results.map((result) => ({
key: cleanKey(pageName, result.key),
value: result.value,
}));
}
async scanPrefixGlobal(
keyPrefix: string
): Promise<{ key: string; value: any }[]> {
let results = await this.pageIndex
.where("key")
.startsWith(keyPrefix)
.toArray();
return results.map((result) => ({
key: result.key,
value: result.value,
}));
}
async deletePrefixForPage(pageName: string, keyPrefix: string) {
await this.pageIndex
.where("ck")
.startsWith(constructKey(pageName, keyPrefix))
.delete();
}
async delete(pageName: string, key: string) {
await this.pageIndex.delete(constructKey(pageName, key));
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"short_name": "Nugget",
"name": "Nugget",
"short_name": "Silver Bullet",
"name": "Silver Bullet",
"icons": [
{
"src": "./images/logo.png",
@@ -9,7 +9,7 @@
}
],
"capture_links": "new-client",
"start_url": "/start",
"start_url": "/",
"display": "standalone",
"scope": "/",
"theme_color": "#000",
+17
View File
@@ -0,0 +1,17 @@
import { manifest, version } from "@parcel/service-worker";
async function install() {
const cache = await caches.open(version);
await cache.addAll(manifest);
}
//@ts-ignore
self.addEventListener("install", (e) => e.waitUntil(install()));
async function activate() {
const keys = await caches.keys();
await Promise.all(keys.map((key) => key !== version && caches.delete(key)));
}
//@ts-ignore
self.addEventListener("activate", (e) => e.waitUntil(activate()));
self.addEventListener("fetch", function (event) {});
+50 -11
View File
@@ -7,14 +7,6 @@ import { CollabEvents, CollabDocument } from "./collab";
import { Cursor, cursorEffect } from "./cursorEffect";
import { EventEmitter } from "./event";
export interface Space {
listPages(): Promise<PageMeta[]>;
readPage(name: string): Promise<{ text: string; meta: PageMeta }>;
writePage(name: string, text: string): Promise<PageMeta>;
deletePage(name: string): Promise<void>;
getPageMeta(name: string): Promise<PageMeta>;
}
export type SpaceEvents = {
connect: () => void;
pageCreated: (meta: PageMeta) => void;
@@ -23,7 +15,12 @@ export type SpaceEvents = {
pageListUpdated: (pages: Set<PageMeta>) => void;
} & CollabEvents;
export class RealtimeSpace extends EventEmitter<SpaceEvents> implements Space {
export type KV = {
key: string;
value: any;
};
export class Space extends EventEmitter<SpaceEvents> {
socket: Socket;
reqId = 0;
allPages = new Set<PageMeta>();
@@ -66,9 +63,15 @@ export class RealtimeSpace extends EventEmitter<SpaceEvents> implements Space {
}
private wsCall(eventName: string, ...args: any[]): Promise<any> {
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
this.reqId++;
this.socket!.once(`${eventName}Resp${this.reqId}`, resolve);
this.socket!.once(`${eventName}Resp${this.reqId}`, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
this.socket!.emit(eventName, this.reqId, ...args);
});
}
@@ -136,4 +139,40 @@ export class RealtimeSpace extends EventEmitter<SpaceEvents> implements Space {
async getPageMeta(name: string): Promise<PageMeta> {
return this.wsCall("deletePage", name);
}
async indexSet(pageName: string, key: string, value: any) {
await this.wsCall("index:set", pageName, key, value);
}
async indexBatchSet(pageName: string, kvs: KV[]) {
// TODO: Optimize with batch call
for (let { key, value } of kvs) {
await this.indexSet(pageName, key, value);
}
}
async indexGet(pageName: string, key: string): Promise<any | null> {
return await this.wsCall("index:get", pageName, key);
}
async indexScanPrefixForPage(
pageName: string,
keyPrefix: string
): Promise<{ key: string; value: any }[]> {
return await this.wsCall("index:scanPrefixForPage", pageName, keyPrefix);
}
async indexScanPrefixGlobal(
keyPrefix: string
): Promise<{ key: string; value: any }[]> {
return await this.wsCall("index:scanPrefixGlobal", keyPrefix);
}
async indexDeletePrefixForPage(pageName: string, keyPrefix: string) {
await this.wsCall("index:deletePrefixForPage", keyPrefix);
}
async indexDelete(pageName: string, key: string) {
await this.wsCall("index:delete", pageName, key);
}
}
+2 -1
View File
@@ -128,7 +128,8 @@
}
.mention {
color: gray;
color: #0330cb;
text-decoration: underline;
}
.tag {
+8 -8
View File
@@ -1,22 +1,22 @@
import { Indexer, KV } from "../indexer";
import { Space, KV } from "../space";
export default (indexer: Indexer) => ({
export default (space: Space) => ({
"indexer.scanPrefixForPage": async (pageName: string, keyPrefix: string) => {
return await indexer.scanPrefixForPage(pageName, keyPrefix);
return await space.indexScanPrefixForPage(pageName, keyPrefix);
},
"indexer.scanPrefixGlobal": async (keyPrefix: string) => {
return await indexer.scanPrefixGlobal(keyPrefix);
return await space.indexScanPrefixGlobal(keyPrefix);
},
"indexer.get": async (pageName: string, key: string): Promise<any> => {
return await indexer.get(pageName, key);
return await space.indexGet(pageName, key);
},
"indexer.set": async (pageName: string, key: string, value: any) => {
await indexer.set(pageName, key, value);
await space.indexSet(pageName, key, value);
},
"indexer.batchSet": async (pageName: string, kvs: KV[]) => {
await indexer.batchSet(pageName, kvs);
await space.indexBatchSet(pageName, kvs);
},
"indexer.delete": async (pageName: string, key: string) => {
await indexer.delete(pageName, key);
await space.indexDelete(pageName, key);
},
});
+1 -1
View File
@@ -18,7 +18,7 @@ export default (editor: Editor) => ({
},
"space.deletePage": async (name: string) => {
console.log("Clearing page index", name);
await editor.indexer.clearPageIndexForPage(name);
await editor.space.indexDeletePrefixForPage(name, "");
// If we're deleting the current page, navigate to the start page
if (editor.currentPage === name) {
await editor.navigate("start");