import {LitElement, html, unsafeHTML, until} from './lit-all.min.js'; import * as tfrpc from '/static/tfrpc.js'; import {styles} from './tf-styles.js'; class TfTabNewsFeedElement extends LitElement { static get properties() { return { whoami: {type: String}, users: {type: Object}, hash: {type: String}, following: {type: Array}, messages: {type: Array}, drafts: {type: Object}, expanded: {type: Object}, }; } static styles = styles; constructor() { super(); let self = this; this.whoami = null; this.users = {}; this.hash = '#'; this.following = []; this.drafts = {}; this.expanded = {}; this.start_time = new Date().valueOf() - 24 * 60 * 60 * 1000; } async fetch_messages() { if (this.hash.startsWith('#@')) { let r = await tfrpc.rpc.query( ` WITH mine AS (SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature FROM messages WHERE messages.author = ? ORDER BY sequence DESC LIMIT 20) SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature FROM mine JOIN messages_refs ON mine.id = messages_refs.ref JOIN messages ON messages_refs.message = messages.id UNION SELECT * FROM mine `, [this.hash.substring(1)] ); return r; } else if (this.hash.startsWith('#%')) { return await tfrpc.rpc.query( ` SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature FROM messages WHERE id = ?1 UNION SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature FROM messages JOIN messages_refs ON messages.id = messages_refs.message WHERE messages_refs.ref = ?1 `, [this.hash.substring(1)] ); } else { let promises = []; const k_following_limit = 256; for (let i = 0; i < this.following.length; i += k_following_limit) { promises.push( tfrpc.rpc.query( ` WITH news AS (SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature FROM messages JOIN json_each(?) AS following ON messages.author = following.value WHERE messages.timestamp > ? AND messages.timestamp < ? ORDER BY messages.timestamp DESC) SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature FROM news JOIN messages_refs ON news.id = messages_refs.ref JOIN messages ON messages_refs.message = messages.id UNION SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature FROM news JOIN messages_refs ON news.id = messages_refs.message JOIN messages ON messages_refs.ref = messages.id UNION SELECT news.* FROM news `, [ JSON.stringify(this.following.slice(i, i + k_following_limit)), this.start_time, /* ** Don't show messages more than a day into the future to prevent ** messages with far-future timestamps from staying at the top forever. */ new Date().valueOf() + 24 * 60 * 60 * 1000, ] ) ); } return [].concat(...(await Promise.all(promises))); } } async load_more() { let last_start_time = this.start_time; this.start_time = last_start_time - 24 * 60 * 60 * 1000; let more = await tfrpc.rpc.query( ` WITH news AS (SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature FROM messages JOIN json_each(?) AS following ON messages.author = following.value WHERE messages.timestamp > ? AND messages.timestamp <= ? ORDER BY messages.timestamp DESC) SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature FROM news JOIN messages_refs ON news.id = messages_refs.ref JOIN messages ON messages_refs.message = messages.id UNION SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature FROM news JOIN messages_refs ON news.id = messages_refs.message JOIN messages ON messages_refs.ref = messages.id UNION SELECT news.* FROM news `, [JSON.stringify(this.following), this.start_time, last_start_time] ); this.messages = await this.decrypt([...more, ...this.messages]); } async decrypt(messages) { console.log('decrypt'); let result = []; for (let message of messages) { let content; try { content = JSON.parse(message?.content); } catch {} if (typeof content === 'string') { let decrypted; try { decrypted = await tfrpc.rpc.try_decrypt(this.whoami, content); } catch {} if (decrypted) { try { message.decrypted = JSON.parse(decrypted); } catch { message.decrypted = decrypted; } } } result.push(message); } return result; } async add_messages(messages) { this.messages = await this.decrypt([...messages, ...this.messages]); } render() { if ( !this.messages || this._messages_hash !== this.hash || this._messages_following !== this.following ) { console.log( `loading messages for ${this.whoami} (following ${this.following.length})` ); let self = this; this.messages = []; this._messages_hash = this.hash; this._messages_following = this.following; this.fetch_messages() .then(this.decrypt.bind(this)) .then(function (messages) { self.messages = messages; console.log(`loading mesages done for ${self.whoami}`); }) .catch(function (error) { alert(JSON.stringify(error, null, 2)); }); } let more; if (!this.hash.startsWith('#@') && !this.hash.startsWith('#%')) { more = html`

`; } return html` ${more} `; } } customElements.define('tf-tab-news-feed', TfTabNewsFeedElement);