add basic observables

This commit is contained in:
tsukino
2023-12-22 16:40:56 -08:00
parent 03ba98f52e
commit 49ccdcf218
5 changed files with 66 additions and 23 deletions

View File

@@ -56,7 +56,7 @@ export type Subscription<ValueType = any> =
}
| ((value: ValueType) => void);
export class Observables<ObservableValue = any> {
export class Observable<ObservableValue = any> {
#state: ObservableValue;
#error: Error | null = null;
#subscriptions: Subscription[] = [];
@@ -114,3 +114,20 @@ export class Observables<ObservableValue = any> {
};
}
}
export class ObservableMap<keyType = string, ValueType = any> {
#map: Map<keyType, Observable<ValueType | null>> = new Map();
get(key: keyType) {
const exist = this.#map.get(key);
if (!exist) {
this.#map.set(key, new Observable(null));
}
return this.#map.get(key);
}
set(key: keyType, value: ValueType) {
const post = this.get(key)!;
post.state = value;
}
}

View File

@@ -36,10 +36,10 @@ export const Q = (root: ShadowRoot | Element) => {
renderFn: (data: any) => Element | DocumentFragment,
) => void;
} => {
const list = Array.prototype.map.call(
const list: any[] = Array.prototype.map.call(
root.querySelectorAll(str),
E,
) as Element[];
);
// @ts-ignore
list.patch = (
@@ -58,8 +58,8 @@ export const Q = (root: ShadowRoot | Element) => {
root.append(renderFn(data));
} else if (last && !data) {
root.removeChild(last);
} else if (lastKey !== currKey) {
root.replaceChild(renderFn(data), last);
} else if (last && lastKey !== currKey) {
root.replaceChild(renderFn(data), last.el);
}
}
};
@@ -74,6 +74,7 @@ const E = (el: Element | null) => {
if (!el) return el;
return {
el: el,
content: (content: string) => {
el.textContent = content;
return el;

View File

@@ -19,14 +19,21 @@ export default class Post extends CustomElement {
async connectedCallback() {
super.connectedCallback();
this.loadPost();
}
async loadPost() {
const store = getStore();
const node = store.get<NodeStore>('node');
const hash = this.dataset.hash!;
const post = await node.getPost(hash);
const post = await node.$posts.get(hash);
const q = Q(this.shadowRoot!);
q.find('div#creator')!.content(post?.json.creator || '');
q.find('div#content')!.content(post?.json.content || '');
q.find('div#createdAt')!.content(post?.json.createdAt.toDateString() || '');
post!.subscribe((p) => {
q.find('div#creator')!.content(p.json.creator || '');
q.find('div#content')!.content(p.json.content || '');
q.find('div#createdAt')!.content(p.json.createdAt.toDateString() || '');
});
}
}

View File

@@ -20,21 +20,25 @@ export default class App extends CustomElement {
async connectedCallback() {
super.connectedCallback();
this.subscribeToPosts();
}
subscribeToPosts() {
const state = getStore();
const node = state.get<NodeState>('node');
const app = this.shadowRoot!.querySelector('div.app')!;
const q = Q(app);
node.posts.subscribe<PostType[]>((posts) => {
node.$globalPosts.subscribe<string[]>((hashes) => {
const app = this.shadowRoot!.querySelector('div.app')!;
const q = Q(app);
const old = q.findAll('post-card');
old.patch(
posts,
(post: PostType) => post.messageId,
(post: PostType) =>
hashes,
(hash: string) => hash,
(hash: string) =>
html(`
<post-card
key="${post.messageId}"
data-hash="${post.hash}"
key="${hash}"
data-hash="${hash}"
/>
`),
);

View File

@@ -1,11 +1,17 @@
import Store, { Observables, type StateOptions } from '../../lib/state.ts';
import Store, {
ObservableMap,
Observable,
type StateOptions,
} from '../../lib/state.ts';
import { Autism } from '@autismjs/protocol/src/services/browser.ts';
import { Post } from '@autismjs/message';
export default class Node extends Store {
node: Autism;
wait: Promise<void>;
posts = new Observables<Post[]>([]);
#wait: Promise<void>;
$globalPosts = new Observable<string[]>([]);
$posts = new ObservableMap<string, Post>();
constructor(options?: StateOptions) {
super(options);
@@ -33,15 +39,23 @@ export default class Node extends Store {
this.updatePosts();
this.wait = new Promise(async (r) => {
this.#wait = new Promise(async (r) => {
await this.node.start();
this.updatePosts();
r();
});
}
async waitForStart() {
return this.#wait;
}
updatePosts = async () => {
this.posts.state = await this.node.db.db.getPosts();
const posts = await this.node.db.db.getPosts();
this.$globalPosts.state = posts.map((p) => {
this.$posts.set(p.hash, p);
return p.hash;
});
};
getPost = async (hash: string): Promise<Post | null> => {