Trying to organize ssblit better.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3980 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		@@ -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"}}
 | 
			
		||||
{"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"}}
 | 
			
		||||
@@ -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();
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -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';
 | 
			
		||||
 
 | 
			
		||||
@@ -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`
 | 
			
		||||
			<tf-id-picker id="picker" selected=${this.whoami} .ids=${this._ids} @change=${this._handle_whoami_changed}></tf-id-picker>
 | 
			
		||||
			<button @click=${this.create_identity}>Create Identity</button>
 | 
			
		||||
		`;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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`
 | 
			
		||||
				<tf-tab-news .following=${following} whoami=${this.whoami} .users=${users} hash=${this.hash} .unread=${this.unread} @refresh=${() => this.unread = []}></tf-tab-news>
 | 
			
		||||
			`;
 | 
			
		||||
		} else if (this.tab === 'connections') {
 | 
			
		||||
			return html`
 | 
			
		||||
				<tf-tab-connections .users=${users} .connections=${this.connections} .broadcasts=${this.broadcasts}></tf-tab-connections>
 | 
			
		||||
			`;
 | 
			
		||||
		} else if (this.tab === 'search') {
 | 
			
		||||
			return html`
 | 
			
		||||
				<tf-tab-search .following=${following} whoami=${this.whoami} .users=${users}></tf-tab-search>
 | 
			
		||||
			`;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render() {
 | 
			
		||||
		let self = this;
 | 
			
		||||
		let id_picker = html`
 | 
			
		||||
			${guard([this.whoami], () => until(this.render_id_picker(), html`<div>Loading...</div>`))}
 | 
			
		||||
		`;
 | 
			
		||||
		let tabs = html`
 | 
			
		||||
			<div>
 | 
			
		||||
				<input type="button" value="News" ?disabled=${self.tab == 'news'} @click=${event => self.tab = 'news'}></input>
 | 
			
		||||
@@ -461,34 +258,11 @@ class TfElement extends LitElement {
 | 
			
		||||
				<input type="button" value="Search" ?disabled=${self.tab == 'search'} @click=${event => self.tab = 'search'}></input>
 | 
			
		||||
			</div>
 | 
			
		||||
		`;
 | 
			
		||||
		let profile = this.hash.startsWith('#@') ?
 | 
			
		||||
			html`<tf-profile id=${this.hash.substring(1)} whoami=${this.whoami} .users=${this.users}></tf-profile>` : undefined;
 | 
			
		||||
		let news = html`
 | 
			
		||||
			<tf-id-picker id="picker" .ids=${this.ids} @change=${this._handle_whoami_changed}></tf-id-picker>
 | 
			
		||||
			<button id="load_button" @click=${this.load}>Load</button>
 | 
			
		||||
			<a target="_top" href="#" ?hidden=${this.hash.length <= 1}>🏠Home</a>
 | 
			
		||||
			<div><input type="button" value=${'Show ' + this.unread.length + ' New Messages'} @click=${this.show_more}></input></div>
 | 
			
		||||
			<div>Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!</div>
 | 
			
		||||
			<div><tf-compose whoami=${this.whoami} .users=${this.users}></tf-compose></div>
 | 
			
		||||
			<div style="font-family: monospace">${this.status.map(x => html`<div>${x.text}...${x.start_time && x.end_time ? 'took ' + Math.round(10 * (x.end_time - x.start_time) / 1000) / 10 + 's' : undefined}</div>`)}</div>
 | 
			
		||||
			${profile}
 | 
			
		||||
			${this.messages?.map(x => html`<tf-message .message=${x} whoami=${this.whoami} .users=${this.users}></tf-message>`)}
 | 
			
		||||
		return html`
 | 
			
		||||
			${id_picker}
 | 
			
		||||
			${tabs}
 | 
			
		||||
			${until(this.render_tab(), html`<div>Loading...</div>`)}
 | 
			
		||||
		`;
 | 
			
		||||
		if (this.tab === 'news') {
 | 
			
		||||
			return html`${tabs}${news}`;
 | 
			
		||||
		} else if (this.tab === 'connections') {
 | 
			
		||||
			return html`
 | 
			
		||||
				${tabs}
 | 
			
		||||
				<tf-connections .users=${this.users} .connections=${this.connections} .broadcasts=${this.broadcasts}></tf-connections>
 | 
			
		||||
			`;
 | 
			
		||||
		} else if (this.tab === 'search') {
 | 
			
		||||
			let search = html`
 | 
			
		||||
				<input type="text" id="search" @keydown=${this.search_keydown}></input>
 | 
			
		||||
				<input type="button" value="Search" @click=${this.search}></input>
 | 
			
		||||
				${this.messages?.map(x => html`<tf-message .message=${x} whoami=${this.whoami} .users=${this.users}></tf-message>`)}
 | 
			
		||||
			`;
 | 
			
		||||
			return html`${tabs}${search}`;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										155
									
								
								apps/cory/ssblit/tf-news.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								apps/cory/ssblit/tf-news.js
									
									
									
									
									
										Normal file
									
								
							@@ -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`<tf-message .message=${x} whoami=${this.whoami} .users=${this.users}></tf-message>`)}
 | 
			
		||||
		`;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render() {
 | 
			
		||||
		let messages = this.load_and_render(this.messages || []);
 | 
			
		||||
		return html`${until(messages, html`<div>Loading placeholders...</div>`)}`;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define('tf-news', TfNewsElement);
 | 
			
		||||
							
								
								
									
										57
									
								
								apps/cory/ssblit/tf-tab-connections.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								apps/cory/ssblit/tf-tab-connections.js
									
									
									
									
									
										Normal file
									
								
							@@ -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`
 | 
			
		||||
			<h2>Broadcasts</h2>
 | 
			
		||||
			<ul>
 | 
			
		||||
				${this.broadcasts.map(x => html`<li><tf-user id=${x.pubkey} .users=${this.users}></tf-user></li>`)}
 | 
			
		||||
			</ul>
 | 
			
		||||
			<h2>Connections</h2>
 | 
			
		||||
			<ul>
 | 
			
		||||
				${this.connections.map(x => html`<li><tf-user id=${x} .users=${this.users}></tf-user></li>`)}
 | 
			
		||||
			</ul>
 | 
			
		||||
			<h2>Local Accounts</h2>
 | 
			
		||||
			<ul>
 | 
			
		||||
				${this.identities.map(x => html`<li><tf-user id=${x} .users=${this.users}></tf-user></li>`)}
 | 
			
		||||
			</ul>
 | 
			
		||||
		`;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define('tf-tab-connections', TfTabConnectionsElement);
 | 
			
		||||
							
								
								
									
										106
									
								
								apps/cory/ssblit/tf-tab-news.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								apps/cory/ssblit/tf-tab-news.js
									
									
									
									
									
										Normal file
									
								
							@@ -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`<tf-news whoami=${this.whoami} .users=${this.users} .messages=${messages}></tf-news>`;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render() {
 | 
			
		||||
		let profile = this.hash.startsWith('#@') ?
 | 
			
		||||
			html`<tf-profile id=${this.hash.substring(1)} whoami=${this.whoami} .users=${this.users}></tf-profile>` : undefined;
 | 
			
		||||
		return html`
 | 
			
		||||
			<div><input type="button" value=${'Show ' + this.unread.length + ' New Messages'} @click=${this.show_more}></input></div>
 | 
			
		||||
			<button id="load_button" @click=${this.load}>Load</button>
 | 
			
		||||
			<a target="_top" href="#" ?hidden=${this.hash.length <= 1}>🏠Home</a>
 | 
			
		||||
			<div>Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!</div>
 | 
			
		||||
			<div><tf-compose whoami=${this.whoami} .users=${this.users}></tf-compose></div>
 | 
			
		||||
			${profile}
 | 
			
		||||
			${until(this.render_news(), html`<div>Loading...</div>`)}
 | 
			
		||||
		`;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define('tf-tab-news', TfTabNewsElement);
 | 
			
		||||
							
								
								
									
										55
									
								
								apps/cory/ssblit/tf-tab-search.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								apps/cory/ssblit/tf-tab-search.js
									
									
									
									
									
										Normal file
									
								
							@@ -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`
 | 
			
		||||
			<input type="text" id="search" @keydown=${this.search_keydown}></input>
 | 
			
		||||
			<input type="button" value="Search" @click=${this.search}></input>
 | 
			
		||||
			<tf-news id="news" whoami=${this.whoami} .messages=${this.messages} .users=${this.users}></tf-news>
 | 
			
		||||
		`;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define('tf-tab-search', TfTabSearchElement);
 | 
			
		||||
		Reference in New Issue
	
	Block a user