From 391da742fdb8287c2b431a97329e145cfa5dcda2 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Wed, 1 Nov 2023 22:21:42 +0000 Subject: [PATCH] Collections progress. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4593 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- apps/collections.json | 2 +- apps/collections/app.js | 26 +++++++ apps/collections/tf-collections-app.js | 71 +++++++++++++++++-- apps/collections/tf-collections.js | 96 +++++++++++++++++--------- apps/collections/tf-wiki-doc.js | 11 +-- 5 files changed, 162 insertions(+), 44 deletions(-) diff --git a/apps/collections.json b/apps/collections.json index 704fbf2e..35c3bbc5 100644 --- a/apps/collections.json +++ b/apps/collections.json @@ -1,5 +1,5 @@ { "type": "tildefriends-app", "emoji": "📦", - "previous": "&tXxiPMH8FZ9m1Jwq37QZi7QMLdFPWLkg6AVyvsJ4V8s=.sha256" + "previous": "&eNhxb2nCe0HSz+sMeiFDY/S8clbarp6P0wmYzBEQ1gI=.sha256" } \ No newline at end of file diff --git a/apps/collections/app.js b/apps/collections/app.js index 151dd25e..6d41c18d 100644 --- a/apps/collections/app.js +++ b/apps/collections/app.js @@ -1,5 +1,7 @@ import * as tfrpc from '/tfrpc.js'; +let g_hash; + tfrpc.register(async function getIdentities() { return ssb.getIdentities(); }); @@ -39,6 +41,30 @@ tfrpc.register(async function get_blob(id) { return utf8Decode(await ssb.blobGet(id)); }); +ssb.addEventListener('message', async function(id) { + let message; + await ssb.sqlAsync('SELECT * FROM messages WHERE id = ?', [id], function(row) { message = row; }); + await tfrpc.rpc.notify_new_message(message); +}); + +core.register('message', async function message_handler(message) { + if (message.event == 'hashChange') { + print('hash change'); + g_hash = message.hash; + await tfrpc.rpc.hash_changed(message.hash); + } +}); + +tfrpc.register(function set_hash(hash) { + if (g_hash != hash) { + return app.setHash(hash); + } +}); + +tfrpc.register(function get_hash(id, message) { + return g_hash; +}); + async function get_collections(kind) { let me = await ssb.getIdentities(); let them = await ssb.following(me, 2); diff --git a/apps/collections/tf-collections-app.js b/apps/collections/tf-collections-app.js index d7878e38..10918543 100644 --- a/apps/collections/tf-collections-app.js +++ b/apps/collections/tf-collections-app.js @@ -6,8 +6,9 @@ class TfCollectionsAppElement extends LitElement { return { ids: {type: Array}, whoami: {type: String}, - wiki: {type: String}, + wiki: {type: Object}, wiki_doc: {type: Object}, + hash: {type: String}, }; } @@ -15,6 +16,14 @@ class TfCollectionsAppElement extends LitElement { super(); this.ids = []; this.load(); + let self = this; + tfrpc.register(function notify_new_message(message) { + self.notify_new_message(message); + }); + tfrpc.register(function hash_changed(hash) { + self.hash = hash; + }); + tfrpc.rpc.get_hash().then(hash => self.hash = hash); } async load() { @@ -22,12 +31,61 @@ class TfCollectionsAppElement extends LitElement { this.whoami = await tfrpc.rpc.localStorageGet('collections_whoami'); } + notify_new_message(message) { + console.log('notify_new_message', message); + console.log('this', this); + console.log('qs', this.shadowRoot.querySelectorAll('tf-collection')); + for (let element of this.shadowRoot.querySelectorAll('tf-collection')) { + element.notify_new_message(message); + } + } + + async notify_hash_changed(hash) { + this.hash = hash; + console.log('notify_hash_changed', hash); + let parts = (hash.startsWith('#') ? hash.substring(1) : hash).split('/'); + console.log('parts', parts); + let wiki = this.shadowRoot.querySelector('tf-collection[type="wiki"]'); + console.log('selecting', wiki, parts[0]); + wiki.set_selected_by_name(parts[0]); + /*console.log('SET wiki', this.wiki); + if (parts.length > 1) { + let wiki_doc = this.shadowRoot.querySelector('tf-collection[type="wiki-doc"]'); + if (wiki_doc) { + this.wiki_doc = wiki_doc.get_by_name(parts[1]); + } + } else { + this.wiki_doc = undefined; + }*/ + } + async on_whoami_changed(event) { let new_id = event.srcElement.selected; await tfrpc.rpc.localStorageSet('collections_whoami', new_id); this.whoami = new_id; } + update_hash() { + tfrpc.rpc.set_hash(this.wiki_doc ? `${this.wiki.name}/${this.wiki_doc.name}` : `${this.wiki.name}`); + } + + async on_wiki_changed(event) { + console.log('wiki changed', event.detail.value); + this.wiki = event.detail.value; + console.log(this.wiki); + if (event.detail.by_user) { + this.update_hash(); + } + } + + async on_wiki_doc_changed(event) { + console.log(event); + this.wiki_doc = event.detail.value; + if (event.detail.by_user) { + this.update_hash(); + } + } + async on_wiki_publish(event) { let blob_id = event.detail.id; let message = { @@ -38,17 +96,18 @@ class TfCollectionsAppElement extends LitElement { }; print(message); await tfrpc.rpc.appendMessage(this.whoami, message); - return this.load(); + return this.shadowRoot.getElementById('docs').load(); } render() { let self = this; + console.log('render', this.wiki?.name, this.wiki_doc?.name, this.hash); return html` -

Hello!

+

Hello! ${this.hash}

- self.wiki = event.detail.id}> - ${this.wiki ? html` self.wiki_doc = event.detail.value}>` : undefined} - ${this.wiki_doc ? html`` : undefined} + + ${this.wiki?.id ? html`` : undefined} + ${this.wiki_doc && this.wiki_doc.parent === this.wiki?.id ? html`` : undefined} `; } } diff --git a/apps/collections/tf-collections.js b/apps/collections/tf-collections.js index 05e14f6b..cb4288b2 100644 --- a/apps/collections/tf-collections.js +++ b/apps/collections/tf-collections.js @@ -1,74 +1,90 @@ import {LitElement, html} from './lit-all.min.js'; import * as tfrpc from '/static/tfrpc.js'; -class TfCollectionsElement extends LitElement { +class TfCollectionElement extends LitElement { static get properties() { return { whoami: {type: String}, collections: {type: Object}, collections_loading: {type: Number}, type: {type: String}, - kind: {type: String}, parent: {type: String}, + selectname: {type: String}, }; } constructor() { super(); this.ids = []; - this.load(); + this.loaded = this.load(); this.type = 'collection'; this.collections_loading = 0; } + process_message(message) { + let content = JSON.parse(message.content); + if (content?.key) { + if (content?.tombstone) { + delete this.by_id[content.key]; + } else if (this.by_id[content.key]) { + this.by_id[content.key] = Object.assign(this.by_id[content.key], content); + } + } else { + this.by_id[message.id] = Object.assign(content, {id: message.id}); + } + } + async load() { try { this.collections_loading++; if (this.whoami) { let following = await tfrpc.rpc.following([this.whoami], 2); - if (this.type || this.kind) { + this.following = following; + if (this.type) { let collections = await tfrpc.rpc.query(` SELECT messages.id, author, content, timestamp FROM messages JOIN json_each(?1) AS id ON messages.author = id.value WHERE json_extract(content, '$.type') = ?2 AND - (?3 IS NULL OR json_extract(content, '$.kind') = ?3) AND - (?4 IS NULL OR json_extract(content, '$.parent') = ?4) + (?3 IS NULL OR json_extract(content, '$.parent') = ?3) ORDER BY timestamp - `, [JSON.stringify(following), this.type, this.kind, this.parent]); - let by_id = {}; + `, [JSON.stringify(following), this.type, this.parent]); + this.by_id = {}; for (let collection of collections) { - let content = JSON.parse(collection.content); - console.log(content); - if (content?.key) { - if (content?.tombstone) { - delete by_id[content.key]; - } else if (by_id[content.key]) { - by_id[content.key] = Object.assign(by_id[content.key], content); - } - } else { - by_id[collection.id] = Object.assign(content, {id: collection.id}); - } + this.process_message(collection); } - this.collections = Object.values(by_id); + this.collections = Object.values(this.by_id); } } + console.log('loaded', this.collections); } finally { this.collections_loading--; } + if (this.selectname) { + this.set_selected_by_name(this.selectname); + } } async create(name, editors) { let message = { type: this.type, - kind: this.kind, name: name, parent: this.parent, editors: editors, }; - print(message); + print('will append', message); await tfrpc.rpc.appendMessage(this.whoami, message); - return this.load(); + } + + notify_new_message(message) { + console.log('got notify new message', message); + if (this.following && + this.following.indexOf(message.author) != -1 && + JSON.parse(message.content).type == this.type) { + console.log('processing message', message); + this.process_message(message); + this.collections = [...Object.values(this.by_id)]; + } } async on_create(event) { @@ -81,8 +97,8 @@ class TfCollectionsElement extends LitElement { async on_tombstone(id) { let message = { type: this.type, - kind: this.kind, key: id, + parent: this.parent, tombstone: { date: new Date().valueOf(), reason: 'archived', @@ -90,23 +106,35 @@ class TfCollectionsElement extends LitElement { }; print(message); await tfrpc.rpc.appendMessage(this.whoami, message); - return this.load(); + //return this.load(); } - set_selected(id, value) { + set_selected(id, value, by_user) { this.dispatchEvent(new CustomEvent('selected', { bubbles: true, detail: { id: id, value: value, + by_user: by_user, }, })); } + set_selected_by_name(name) { + if (this.collections) { + for (let collection of this.collections) { + if (collection.name === name) { + this.set_selected(collection.id, collection); + } + } + } + this._select_by_name = name; + } + render_collection(collection) { return html`
- + ${collection.id}
@@ -117,15 +145,17 @@ class TfCollectionsElement extends LitElement { } render() { - let state = JSON.stringify([this.whoami, this.ids, this.kind, this.parent]); + let state = JSON.stringify([this.whoami, this.ids, this.parent]); if (state !== this.loaded_for) { this.loaded_for = state; - this.load(); + console.log('start load', state); + this.loaded = this.load(); } return html` -

${this.kind ?? this.type}s

-
${this.whoami}
- +

${this.type}s

+
${this.whoami} ${this.selectname}
+
${this.parent}
+ ${this.collections_loading ? html`
Loading...
` : html`