diff --git a/apps/cory/ssblit.json b/apps/cory/ssblit.json index 8d218e10..3f800199 100644 --- a/apps/cory/ssblit.json +++ b/apps/cory/ssblit.json @@ -1 +1 @@ -{"type":"tildefriends-app","files":{"app.js":"&b8IFBOMDtcvY5XNtUQIUeoE+++/TO8LDp86xNFIaux8=.sha256","lit-all.min.js":"&N4A12AsifdQgwdpII0SFtG513BfoLpmPjdJ9VTDftpg=.sha256","index.html":"&WH8A5tF25xlfPDGei2TCQc2/HJFJf5DuRN1GRSYQhhk=.sha256","script.js":"&diQfpbxjgd/jSPnIoAoWT75B8Pll1I5JYXhu+/phj9k=.sha256","lit-all.min.js.map":"&oFY9wO4MnujgfGNGv4VggHc5V5JwX4C8csqKZ6KJYbE=.sha256","tf-id-picker.js":"&ewIlLZNhaHm2dztxqj2Ft38WZkNPQxYfOGBrwTDUhds=.sha256","tf-app.js":"&Ss7Cw1jQkpqk5ckTTndYFtIMrEQRDK9vmMWi2nALCt0=.sha256","tf-message.js":"&KE1fWTqPMZR0yIRXPBGy8u1chR6LTguSK6swo+lFgE4=.sha256","tf-user.js":"&L6+7BnBq+UOoTMO6o8+u5JFTl0UBtCPDw8bb8ppDrkA=.sha256","tf-utils.js":"&N2yKZwFnb2GbPeipgQtu6xFvezENNOgud9G7EhCQ/K0=.sha256","commonmark.min.js":"&bfBaMLU19d1p/vPBF9hlARqDX002KXG/UOfxOahZhe4=.sha256","tf-compose.js":"&oo0iWvT+c2rU91zWpBIfPePRzmU8qmSnVOm+QCQqG/I=.sha256","emojis.json":"&h3P4pez+AI4aYdsN0dJ3pbUEFR0276t9AM20caj/W/s=.sha256","emojis.js":"&htPMi2z6Bmgi3f9jCnECCDZRCHACnDRjOl1kgPm+W80=.sha256","tf-styles.js":"&BkvFkMpGyL0DYP6FISFKR4pe6ZBOp8t6tQEzWZ4IQYs=.sha256","tf-profile.js":"&vRKjsnYvOiHCQahzEfznCvP5YDwUPtltlpWf+pxwZ1Y=.sha256","commonmark-linkify.js":"&X+hNNkmSRvKY86khyAun+cXksquXbMakZdINbGbx30g=.sha256","tf-connections.js":"&YUD4n/r95AwD2fA63HHE2eQt4E/27gF+4/MYrdvoasw=.sha256"}} \ No newline at end of file +{"type":"tildefriends-app","files":{"app.js":"&viCT+Sz8weP/j5V47w0wA4sk46HM4uy6lajX5NtoqHE=.sha256","lit-all.min.js":"&N4A12AsifdQgwdpII0SFtG513BfoLpmPjdJ9VTDftpg=.sha256","index.html":"&WH8A5tF25xlfPDGei2TCQc2/HJFJf5DuRN1GRSYQhhk=.sha256","script.js":"&G8puK9Q4MngHy3D4ppcKyT49WKbHD2OCeUcAw2ghTDE=.sha256","lit-all.min.js.map":"&oFY9wO4MnujgfGNGv4VggHc5V5JwX4C8csqKZ6KJYbE=.sha256","tf-id-picker.js":"&pg1gLK150HFai73TcmAe5E/dMpMqmbhyre/+/J4XmHo=.sha256","tf-app.js":"&Zt1x1urnzk0D9TxQvgJqOjdHelW+bei7CRupODgvAsk=.sha256","tf-message.js":"&KE1fWTqPMZR0yIRXPBGy8u1chR6LTguSK6swo+lFgE4=.sha256","tf-user.js":"&L6+7BnBq+UOoTMO6o8+u5JFTl0UBtCPDw8bb8ppDrkA=.sha256","tf-utils.js":"&N2yKZwFnb2GbPeipgQtu6xFvezENNOgud9G7EhCQ/K0=.sha256","commonmark.min.js":"&bfBaMLU19d1p/vPBF9hlARqDX002KXG/UOfxOahZhe4=.sha256","tf-compose.js":"&oo0iWvT+c2rU91zWpBIfPePRzmU8qmSnVOm+QCQqG/I=.sha256","emojis.json":"&h3P4pez+AI4aYdsN0dJ3pbUEFR0276t9AM20caj/W/s=.sha256","emojis.js":"&htPMi2z6Bmgi3f9jCnECCDZRCHACnDRjOl1kgPm+W80=.sha256","tf-styles.js":"&BkvFkMpGyL0DYP6FISFKR4pe6ZBOp8t6tQEzWZ4IQYs=.sha256","tf-profile.js":"&vRKjsnYvOiHCQahzEfznCvP5YDwUPtltlpWf+pxwZ1Y=.sha256","commonmark-linkify.js":"&X+hNNkmSRvKY86khyAun+cXksquXbMakZdINbGbx30g=.sha256","tf-tab-search.js":"&Q4bstnLzTPmCKJP+cf7FfRZJVuGAltEely4oIovUVaI=.sha256","tf-tab-news.js":"&Pc/FkHOPRPyWZi/znjquVtXeykzqqFygVuNm0dxrwlI=.sha256","tf-tab-connections.js":"&jSnF/5NmgqxRze1XQAEGOW5mPzOV1/8aCyrDRZu34IQ=.sha256","tf-news.js":"&C1dKe98kQOkClnAbGvcreC15IdlTrD9J4RFohspnsSE=.sha256"}} \ No newline at end of file diff --git a/apps/cory/ssblit/app.js b/apps/cory/ssblit/app.js index c06d8d28..9f4ec03f 100644 --- a/apps/cory/ssblit/app.js +++ b/apps/cory/ssblit/app.js @@ -15,6 +15,9 @@ tfrpc.register(async function databaseGet(key) { tfrpc.register(async function databaseSet(key, value) { return g_database ? g_database.set(key, value) : undefined; }); +tfrpc.register(async function createIdentity() { + return ssb.createIdentity(); +}); tfrpc.register(async function getIdentities() { return ssb.getIdentities(); }); diff --git a/apps/cory/ssblit/script.js b/apps/cory/ssblit/script.js index 7c16e66f..7730bce9 100644 --- a/apps/cory/ssblit/script.js +++ b/apps/cory/ssblit/script.js @@ -6,5 +6,8 @@ import * as tf_app from './tf-app.js'; import * as tf_message from './tf-message.js'; import * as tf_user from './tf-user.js'; import * as tf_compose from './tf-compose.js'; +import * as tf_news from './tf-news.js'; import * as tf_profile from './tf-profile.js'; -import * as tf_connections from './tf-connections.js'; +import * as tf_tab_news from './tf-tab-news.js'; +import * as tf_tab_search from './tf-tab-search.js'; +import * as tf_tab_connections from './tf-tab-connections.js'; diff --git a/apps/cory/ssblit/tf-app.js b/apps/cory/ssblit/tf-app.js index 57407327..a3b2e7c8 100644 --- a/apps/cory/ssblit/tf-app.js +++ b/apps/cory/ssblit/tf-app.js @@ -1,4 +1,4 @@ -import {LitElement, html, css} from './lit-all.min.js'; +import {LitElement, html, css, guard, until} from './lit-all.min.js'; import * as tfrpc from '/static/tfrpc.js'; import {styles} from './tf-styles.js'; @@ -6,11 +6,6 @@ class TfElement extends LitElement { static get properties() { return { whoami: {type: String}, - ids: {type: Array}, - messages: {type: Array}, - users: {type: Object}, - allFollowing: {type: Array}, - status: {type: Array}, hash: {type: String}, unread: {type: Array}, tab: {type: String}, @@ -24,25 +19,16 @@ class TfElement extends LitElement { constructor() { super(); let self = this; - this.ids = []; - this.users = {}; - this.messages = []; - this.allFollowing = []; - this.status = []; - this.messages_by_id = {}; this.hash = '#'; - this.loading = false; this.unread = []; this.tab = 'news'; this.broadcasts = []; this.connections = []; - tfrpc.rpc.getIdentities().then(ids => { self.ids = ids || [] }); tfrpc.rpc.getBroadcasts().then(b => { self.broadcasts = b || [] }); tfrpc.rpc.getConnections().then(c => { self.connections = c || [] }); tfrpc.rpc.getHash().then(hash => self.hash = hash || '#'); tfrpc.register(function hashChanged(hash) { self.hash = hash; - self.load(); }); tfrpc.register(async function notifyNewMessage(id) { await self.fetch_new_message(id); @@ -54,6 +40,9 @@ class TfElement extends LitElement { self.connections = value; } }); + tfrpc.rpc.localStorageGet('whoami').then(function(value) { + self.whoami = value; + }); } async contacts_internal(id, last_row_id, following, max_row_id) { @@ -85,27 +74,21 @@ class TfElement extends LitElement { return result; } - async contact(id, last_row_id, following, max_row_id) { - if (this.users[id]?.following) { - return this.users[id]; - } - + async contact(id, last_row_id, following, max_row_id, contact_cache) { let result = await this.contacts_internal(id, last_row_id, following, max_row_id); - let users = this.users; - users[id] = Object.assign(users[id] || {}, result); - following[id] = users[id]; - this.users = users; + contact_cache[id] = Object.assign(contact_cache[id] || {}, result); + following[id] = contact_cache[id]; return result; } - async following_deep_internal(ids, depth, blocking, last_row_id, following, max_row_id) { - let contacts = await Promise.all([...new Set(ids)].map(x => this.contact(x, last_row_id, following, max_row_id))); + async following_deep_internal(ids, depth, blocking, last_row_id, following, max_row_id, contact_cache) { + let contacts = await Promise.all([...new Set(ids)].map(x => this.contact(x, last_row_id, following, max_row_id, contact_cache))); let result = {}; for (let i = 0; i < ids.length; i++) { let id = ids[i]; let contact = contacts[i]; let found = Object.keys(contact.following).filter(y => !contact.blocking[y]); - let deeper = depth > 1 ? await this.following_deep_internal(found, depth - 1, Object.assign({}, contact.blocking, blocking), last_row_id, following, max_row_id) : []; + let deeper = depth > 1 ? await this.following_deep_internal(found, depth - 1, Object.assign({}, contact.blocking, blocking), last_row_id, following, max_row_id, contact_cache) : []; result[id] = [id, ...found, ...deeper]; } return [...new Set(Object.values(result).flat())]; @@ -122,16 +105,17 @@ class TfElement extends LitElement { last_row_id: 0, }; } + let contact_cache = {}; let max_row_id = (await tfrpc.rpc.query(` SELECT MAX(rowid) AS max_row_id FROM messages `, []))[0].max_row_id; - let result = await this.following_deep_internal(ids, depth, blocking, cache.last_row_id, cache.following, max_row_id); + let result = await this.following_deep_internal(ids, depth, blocking, cache.last_row_id, cache.following, max_row_id, contact_cache); cache.last_row_id = max_row_id; await tfrpc.rpc.databaseSet('following', JSON.stringify(cache)); return result; } - async fetch_about(ids) { + async fetch_about(ids, users) { const k_cache_version = 1; let cache = await tfrpc.rpc.databaseGet('about'); cache = cache ? JSON.parse(cache) : {}; @@ -191,50 +175,11 @@ class TfElement extends LitElement { } cache.last_row_id = max_row_id; await tfrpc.rpc.databaseSet('about', JSON.stringify(cache)); - let users = this.users || {}; + users = users || {}; for (let id of Object.keys(cache.about)) { users[id] = Object.assign(users[id] || {}, cache.about[id]); } - this.users = Object.assign({}, users); - } - - async fetch_messages() { - if (this.hash.startsWith('#@')) { - return await tfrpc.rpc.query( - ` - SELECT messages.* - FROM messages - WHERE messages.author = ? - ORDER BY sequence DESC - LIMIT 20 - `, - [ - this.hash.substring(1), - ]); - } else if (this.hash.startsWith('#%')) { - return await tfrpc.rpc.query( - ` - SELECT messages.* - FROM messages - WHERE id = ? - `, - [ - this.hash.substring(1), - ]); - } else { - return await tfrpc.rpc.query( - ` - SELECT messages.* - FROM messages - JOIN json_each(?) AS following ON messages.author = following.value - WHERE messages.timestamp > ? - ORDER BY messages.timestamp DESC - `, - [ - JSON.stringify(this.allFollowing), - new Date().valueOf() - 24 * 60 * 60 * 1000, - ]); - } + return Object.assign({}, users); } async fetch_new_message(id) { @@ -261,199 +206,51 @@ class TfElement extends LitElement { } } - async show_more() { - let unread = this.unread; - this.unread = []; - this.process_messages(unread); - await this.finalize_messages(); - } - - record_status(text) { - let now = new Date(); - if (this.status.length) { - this.status[this.status.length - 1].end_time = now; - console.log( - this.status[this.status.length - 1].text, - (now - this.status[this.status.length - 1].start_time).valueOf()); - } - this.status.push({ - text: text, - start_time: now, - }); - } - - ensure_message(id) { - let found = this.messages_by_id[id]; - if (found) { - return found; - } else { - let added = { - id: id, - placeholder: true, - content: '"placeholder"', - parent_message: undefined, - child_messages: [], - votes: [], - }; - this.messages_by_id[id] = added; - return added; - } - } - - process_messages(messages) { - let self = this; - - function link_message(message) { - if (message.content.type === 'vote') { - let parent = self.ensure_message(message.content.vote.link); - if (!parent.votes) { - parent.votes = []; - } - parent.votes.push(message); - message.parent_message = message.content.vote.link; - } else if (message.content.type == 'post') { - if (message.content.root) { - if (typeof(message.content.root) === 'string') { - let m = self.ensure_message(message.content.root); - if (!m.child_messages) { - m.child_messages = []; - } - m.child_messages.push(message); - message.parent_message = message.content.root; - } else { - let m = self.ensure_message(message.content.root[0]); - if (!m.child_messages) { - m.child_messages = []; - } - m.child_messages.push(message); - message.parent_message = message.content.root[0]; - } - } - } - } - - for (let message of messages) { - message.content = JSON.parse(message.content); - if (!this.messages_by_id[message.id]) { - this.messages_by_id[message.id] = message; - link_message(message); - } else if (this.messages_by_id[message.id].placeholder) { - let placeholder = this.messages_by_id[message.id]; - this.messages_by_id[message.id] = message; - message.parent_message = placeholder.parent_message; - message.child_messages = placeholder.child_messages; - message.votes = placeholder.votes; - if (placeholder.parent_message && this.messages_by_id[placeholder.parent_message]) { - let children = this.messages_by_id[placeholder.parent_message].child_messages; - children.splice(children.indexOf(placeholder), 1); - children.push(message); - } - link_message(message); - } - } - } - - async load_placeholders() { - let placeholders = Object.values(this.messages_by_id).filter(x => x.placeholder).map(x => x.id); - return await tfrpc.rpc.query( - ` - SELECT messages.* FROM messages - JOIN json_each(?) AS placeholder ON messages.id = placeholder.value - JOIN json_each(?) AS following ON messages.author = following.value - ORDER BY messages.timestamp DESC - `, - [ - JSON.stringify(placeholders), - JSON.stringify(this.allFollowing), - ]); - } - - async finalize_messages() { - this.process_messages(await this.load_placeholders()); - function recursive_sort(messages, top) { - if (messages) { - if (top) { - messages.sort((a, b) => b.timestamp - a.timestamp); - } else { - messages.sort((a, b) => a.timestamp - b.timestamp); - } - for (let message of messages) { - recursive_sort(message.child_messages, false); - } - return messages.map(x => Object.assign({}, x)); - } - } - this.messages = - recursive_sort( - Object.values(this.messages_by_id) - .filter(x => !x.parent_message), - true); - } - - async load() { - if (this.loading || (!this.whoami && this.ids.length)) { - return; - } - let load_button = this.renderRoot.getElementById('load_button'); - this.loading = true; - if (load_button) { - load_button.disabled = true; - } - this.status = []; - this.messages = []; - this.messages_by_id = {}; - this.users = {}; - this.allFollowing = []; - console.log('loading...', this.hash); - this.record_status('loading'); - this.record_status('getting following'); - this.allFollowing = await this.following_deep([this.whoami], 2, {}); - console.log('following', this.allFollowing.length, 'identities'); - this.record_status('getting about'); - await this.fetch_about(this.allFollowing.sort()); - this.record_status('getting messages'); - this.process_messages(await this.fetch_messages()); - await this.finalize_messages(); - this.record_status('done'); - this.status = []; - if (load_button) { - load_button.disabled = false; - } - this.loading = false; - } - _handle_whoami_changed(event) { - this.whoami = event.srcElement.selected; - this.load(); + if (this.whoami !== event.srcElement.selected) { + console.log('whoami changed', event.srcElement.selected); + this.whoami = event.srcElement.selected; + } } - async search(event) { - this.messages = []; - this.messages_by_id = {}; - let query = this.renderRoot.getElementById('search').value; - console.log('Searching...'); - let results = await tfrpc.rpc.query(` - SELECT messages.* - FROM messages_fts(?) - JOIN messages ON messages.rowid = messages_fts.rowid - JOIN json_each(?) AS following ON messages.author = following.value - ORDER BY timestamp DESC limit 100 - `, - [query, JSON.stringify(this.allFollowing)]); - console.log('Done.'); - this.process_messages(results); - await this.finalize_messages(); - this.renderRoot.getElementById('search').value = ''; + async create_identity() { + if (confirm("Are you sure you want to create a new identity?")) { + await tfrpc.rpc.createIdentity(); + this.requestUpdate(); + } } - search_keydown(event) { - if (event.keyCode == 13) { - this.search(); + async render_id_picker() { + this._ids = this._ids || (await tfrpc.rpc.getIdentities()) || []; + return html` + + + `; + } + + async render_tab() { + let following = await this.following_deep([this.whoami], 2, {}); + let users = await this.fetch_about(following.sort()); + if (this.tab === 'news') { + return html` + this.unread = []}> + `; + } else if (this.tab === 'connections') { + return html` + + `; + } else if (this.tab === 'search') { + return html` + + `; } } render() { let self = this; + let id_picker = html` + ${guard([this.whoami], () => until(this.render_id_picker(), html`
Loading...
`))} + `; let tabs = html`
self.tab = 'news'}> @@ -461,34 +258,11 @@ class TfElement extends LitElement { self.tab = 'search'}>
`; - let profile = this.hash.startsWith('#@') ? - html`` : undefined; - let news = html` - - - 🏠Home -
-
Welcome, !
-
-
${this.status.map(x => html`
${x.text}...${x.start_time && x.end_time ? 'took ' + Math.round(10 * (x.end_time - x.start_time) / 1000) / 10 + 's' : undefined}
`)}
- ${profile} - ${this.messages?.map(x => html``)} + return html` + ${id_picker} + ${tabs} + ${until(this.render_tab(), html`
Loading...
`)} `; - if (this.tab === 'news') { - return html`${tabs}${news}`; - } else if (this.tab === 'connections') { - return html` - ${tabs} - - `; - } else if (this.tab === 'search') { - let search = html` - - - ${this.messages?.map(x => html``)} - `; - return html`${tabs}${search}`; - } } } diff --git a/apps/cory/ssblit/tf-id-picker.js b/apps/cory/ssblit/tf-id-picker.js index 3bc67464..861c57d2 100644 --- a/apps/cory/ssblit/tf-id-picker.js +++ b/apps/cory/ssblit/tf-id-picker.js @@ -17,10 +17,6 @@ class TfIdentityPickerElement extends LitElement { super(); let self = this; this.ids = []; - tfrpc.rpc.localStorageGet('whoami').then(function(selected) { - self.selected = selected; - self._emit_change(); - }); } _emit_change() { diff --git a/apps/cory/ssblit/tf-news.js b/apps/cory/ssblit/tf-news.js new file mode 100644 index 00000000..774a3a01 --- /dev/null +++ b/apps/cory/ssblit/tf-news.js @@ -0,0 +1,155 @@ +import {LitElement, html, unsafeHTML, until} from './lit-all.min.js'; +import * as tfrpc from '/static/tfrpc.js'; +import {styles} from './tf-styles.js'; + +class TfNewsElement extends LitElement { + static get properties() { + return { + whoami: {type: String}, + users: {type: Object}, + messages: {type: Array}, + following: {type: Array}, + } + } + + static styles = styles; + + constructor() { + super(); + let self = this; + this.whoami = null; + this.users = {}; + this.messages = []; + this.following = []; + } + + process_messages(messages, in_messages_by_id) { + let self = this; + let messages_by_id = Object.assign({}, in_messages_by_id || {}); + + function ensure_message(id) { + let found = messages_by_id[id]; + if (found) { + return found; + } else { + let added = { + id: id, + placeholder: true, + content: '"placeholder"', + parent_message: undefined, + child_messages: [], + votes: [], + }; + messages_by_id[id] = added; + return added; + } + } + + function link_message(message) { + if (message.content.type === 'vote') { + let parent = ensure_message(message.content.vote.link); + if (!parent.votes) { + parent.votes = []; + } + parent.votes.push(message); + message.parent_message = message.content.vote.link; + } else if (message.content.type == 'post') { + if (message.content.root) { + if (typeof(message.content.root) === 'string') { + let m = ensure_message(message.content.root); + if (!m.child_messages) { + m.child_messages = []; + } + m.child_messages.push(message); + message.parent_message = message.content.root; + } else { + let m = ensure_message(message.content.root[0]); + if (!m.child_messages) { + m.child_messages = []; + } + m.child_messages.push(message); + message.parent_message = message.content.root[0]; + } + } + } + } + + for (let message of messages) { + try { + message.content = JSON.parse(message.content); + } catch { + } + if (!messages_by_id[message.id]) { + messages_by_id[message.id] = message; + link_message(message); + } else if (messages_by_id[message.id].placeholder) { + let placeholder = messages_by_id[message.id]; + messages_by_id[message.id] = message; + message.parent_message = placeholder.parent_message; + message.child_messages = placeholder.child_messages; + message.votes = placeholder.votes; + if (placeholder.parent_message && messages_by_id[placeholder.parent_message]) { + let children = messages_by_id[placeholder.parent_message].child_messages; + children.splice(children.indexOf(placeholder), 1); + children.push(message); + } + link_message(message); + } + } + + return messages_by_id; + } + + async load_placeholders(messages_by_id) { + let placeholders = Object.values(messages_by_id).filter(x => x.placeholder).map(x => x.id); + return await tfrpc.rpc.query( + ` + SELECT messages.* FROM messages + JOIN json_each(?) AS placeholder ON messages.id = placeholder.value + JOIN json_each(?) AS following ON messages.author = following.value + ORDER BY messages.timestamp DESC + `, + [ + JSON.stringify(placeholders), + JSON.stringify(this.following), + ]); + } + + finalize_messages(messages_by_id) { + function recursive_sort(messages, top) { + if (messages) { + if (top) { + messages.sort((a, b) => b.timestamp - a.timestamp); + } else { + messages.sort((a, b) => a.timestamp - b.timestamp); + } + for (let message of messages) { + recursive_sort(message.child_messages, false); + } + return messages.map(x => Object.assign({}, x)); + } else { + return {}; + } + } + + let roots = Object.values(messages_by_id).filter(x => !x.parent_message); + return recursive_sort(roots, true); + } + + async load_and_render(messages) { + let messages_by_id = this.process_messages(messages); + let placeholders = await this.load_placeholders(messages_by_id); + messages_by_id = this.process_messages(placeholders, messages_by_id); + let final_messages = this.finalize_messages(messages_by_id); + return html` + ${final_messages.map(x => html``)} + `; + } + + render() { + let messages = this.load_and_render(this.messages || []); + return html`${until(messages, html`
Loading placeholders...
`)}`; + } +} + +customElements.define('tf-news', TfNewsElement); \ No newline at end of file diff --git a/apps/cory/ssblit/tf-tab-connections.js b/apps/cory/ssblit/tf-tab-connections.js new file mode 100644 index 00000000..07810df1 --- /dev/null +++ b/apps/cory/ssblit/tf-tab-connections.js @@ -0,0 +1,57 @@ +import {LitElement, html} from './lit-all.min.js'; +import * as tfrpc from '/static/tfrpc.js'; + +class TfTabConnectionsElement extends LitElement { + static get properties() { + return { + broadcasts: {type: Array}, + identities: {type: Array}, + connections: {type: Array}, + users: {type: Object}, + } + } + + constructor() { + super(); + let self = this; + this.broadcasts = []; + this.identities = []; + this.connections = []; + this.users = {}; + tfrpc.rpc.getAllIdentities().then(function(identities) { + self.identities = identities || []; + }); + } + + _emit_change() { + let changed_event = new Event('change', { + srcElement: this, + }); + this.dispatchEvent(changed_event); + } + + changed(event) { + this.selected = event.srcElement.value; + tfrpc.rpc.localStorageSet('whoami', this.selected); + this._emit_change(); + } + + render() { + return html` +

Broadcasts

+ +

Connections

+ +

Local Accounts

+ + `; + } +} + +customElements.define('tf-tab-connections', TfTabConnectionsElement); \ No newline at end of file diff --git a/apps/cory/ssblit/tf-tab-news.js b/apps/cory/ssblit/tf-tab-news.js new file mode 100644 index 00000000..4e52fbf5 --- /dev/null +++ b/apps/cory/ssblit/tf-tab-news.js @@ -0,0 +1,106 @@ +import {LitElement, html, unsafeHTML, until} from './lit-all.min.js'; +import * as tfrpc from '/static/tfrpc.js'; +import {styles} from './tf-styles.js'; + +class TfTabNewsElement extends LitElement { + static get properties() { + return { + whoami: {type: String}, + users: {type: Object}, + hash: {type: String}, + unread: {type: Array}, + following: {type: Array}, + } + } + + static styles = styles; + + constructor() { + super(); + let self = this; + this.whoami = null; + this.users = {}; + this.hash = '#'; + this.unread = []; + this.following = []; + this.cache = {}; + } + + async fetch_messages() { + if (this.hash.startsWith('#@')) { + return await tfrpc.rpc.query( + ` + SELECT messages.* + FROM messages + WHERE messages.author = ? + ORDER BY sequence DESC + LIMIT 20 + `, + [ + this.hash.substring(1), + ]); + } else if (this.hash.startsWith('#%')) { + return await tfrpc.rpc.query( + ` + SELECT messages.* + FROM messages + WHERE id = ? + `, + [ + this.hash.substring(1), + ]); + } else { + return await tfrpc.rpc.query( + ` + SELECT messages.* + FROM messages + JOIN json_each(?) AS following ON messages.author = following.value + WHERE messages.timestamp > ? + ORDER BY messages.timestamp DESC + `, + [ + JSON.stringify(this.following), + new Date().valueOf() - 24 * 60 * 60 * 1000, + ]); + } + } + + async show_more() { + let unread = this.unread; + this.unread = []; + this.process_messages(unread); + await this.finalize_messages(); + } + + async render_news() { + if (this.cache.hash !== this.hash || + this.cache.whoami !== this.whoami || + this.cache.users !== this.users || + !this.cache.messages) { + this.cache = { + hash: this.hash, + whoami: this.whoami, + users: this.users, + messages: this.fetch_messages(), + }; + } + let messages = await this.cache.messages; + return html``; + } + + render() { + let profile = this.hash.startsWith('#@') ? + html`` : undefined; + return html` +
+ + 🏠Home +
Welcome, !
+
+ ${profile} + ${until(this.render_news(), html`
Loading...
`)} + `; + } +} + +customElements.define('tf-tab-news', TfTabNewsElement); \ No newline at end of file diff --git a/apps/cory/ssblit/tf-tab-search.js b/apps/cory/ssblit/tf-tab-search.js new file mode 100644 index 00000000..83361d96 --- /dev/null +++ b/apps/cory/ssblit/tf-tab-search.js @@ -0,0 +1,55 @@ +import {LitElement, html, unsafeHTML} from './lit-all.min.js'; +import * as tfrpc from '/static/tfrpc.js'; +import {styles} from './tf-styles.js'; + +class TfTabSearchElement extends LitElement { + static get properties() { + return { + whoami: {type: String}, + users: {type: Object}, + following: {type: Array}, + } + } + + static styles = styles; + + constructor() { + super(); + let self = this; + this.whoami = null; + this.users = {}; + this.following = []; + } + + async search(event) { + let query = this.renderRoot.getElementById('search').value; + console.log('Searching...', this.whoami, query); + let results = await tfrpc.rpc.query(` + SELECT messages.* + FROM messages_fts(?) + JOIN messages ON messages.rowid = messages_fts.rowid + JOIN json_each(?) AS following ON messages.author = following.value + ORDER BY timestamp DESC limit 100 + `, + [query, JSON.stringify(this.following)]); + console.log('Done.'); + this.renderRoot.getElementById('search').value = ''; + this.renderRoot.getElementById('news').messages = results; + } + + search_keydown(event) { + if (event.keyCode == 13) { + this.search(); + } + } + + render() { + return html` + + + + `; + } +} + +customElements.define('tf-tab-search', TfTabSearchElement); \ No newline at end of file