All checks were successful
		
		
	
	Build Tilde Friends / Build-All (push) Successful in 18m1s
				
			
		
			
				
	
	
		
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
 | 
						|
import * as tfrpc from '/static/tfrpc.js';
 | 
						|
import {styles, generate_theme} from './tf-styles.js';
 | 
						|
 | 
						|
class TfTabSearchElement extends LitElement {
 | 
						|
	static get properties() {
 | 
						|
		return {
 | 
						|
			drafts: {type: Object},
 | 
						|
			whoami: {type: String},
 | 
						|
			users: {type: Object},
 | 
						|
			following: {type: Array},
 | 
						|
			query: {type: String},
 | 
						|
			expanded: {type: Object},
 | 
						|
			messages: {type: Array},
 | 
						|
			results: {type: Array},
 | 
						|
			error: {type: Object},
 | 
						|
		};
 | 
						|
	}
 | 
						|
 | 
						|
	static styles = styles;
 | 
						|
 | 
						|
	constructor() {
 | 
						|
		super();
 | 
						|
		let self = this;
 | 
						|
		this.whoami = null;
 | 
						|
		this.users = {};
 | 
						|
		this.following = [];
 | 
						|
		this.expanded = {};
 | 
						|
		this.drafts = {};
 | 
						|
		tfrpc.rpc.localStorageGet('drafts').then(function (d) {
 | 
						|
			self.drafts = JSON.parse(d || '{}');
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	async search(query) {
 | 
						|
		console.log('Searching...', this.whoami, query);
 | 
						|
		let search = this.renderRoot.getElementById('search');
 | 
						|
		if (search) {
 | 
						|
			search.value = query;
 | 
						|
			search.focus();
 | 
						|
			search.select();
 | 
						|
		}
 | 
						|
		await tfrpc.rpc.setHash('#q=' + encodeURIComponent(query));
 | 
						|
		this.error = undefined;
 | 
						|
		this.results = [];
 | 
						|
		this.messages = [];
 | 
						|
		if (query.startsWith('sql:')) {
 | 
						|
			this.messages = [];
 | 
						|
			try {
 | 
						|
				this.results = await tfrpc.rpc.query(
 | 
						|
					query.substring('sql:'.length),
 | 
						|
					[]
 | 
						|
				);
 | 
						|
			} catch (e) {
 | 
						|
				this.results = [];
 | 
						|
				this.error = e;
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			let results = await tfrpc.rpc.query(
 | 
						|
				`
 | 
						|
					SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
 | 
						|
					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.replace('"', '""') + '"', JSON.stringify(this.following)]
 | 
						|
			);
 | 
						|
			console.log('Done.');
 | 
						|
			search = this.renderRoot.getElementById('search');
 | 
						|
			if (search) {
 | 
						|
				search.value = query;
 | 
						|
				search.focus();
 | 
						|
				search.select();
 | 
						|
			}
 | 
						|
			this.messages = results;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	search_keydown(event) {
 | 
						|
		if (event.keyCode == 13) {
 | 
						|
			this.query = this.renderRoot.getElementById('search').value;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	on_expand(event) {
 | 
						|
		if (event.detail.expanded) {
 | 
						|
			let expand = {};
 | 
						|
			expand[event.detail.id] = true;
 | 
						|
			this.expanded = Object.assign({}, this.expanded, expand);
 | 
						|
		} else {
 | 
						|
			delete this.expanded[event.detail.id];
 | 
						|
			this.expanded = Object.assign({}, this.expanded);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	draft(event) {
 | 
						|
		let id = event.detail.id || '';
 | 
						|
		let previous = this.drafts[id];
 | 
						|
		if (event.detail.draft !== undefined) {
 | 
						|
			this.drafts[id] = event.detail.draft;
 | 
						|
		} else {
 | 
						|
			delete this.drafts[id];
 | 
						|
		}
 | 
						|
		this.drafts = Object.assign({}, this.drafts);
 | 
						|
		tfrpc.rpc.localStorageSet('drafts', JSON.stringify(this.drafts));
 | 
						|
	}
 | 
						|
 | 
						|
	render_results() {
 | 
						|
		if (this.error) {
 | 
						|
			return html`<h2 style="color: red">${this.error.message}</h2>
 | 
						|
				<pre style="color: red">${this.error.stack}</pre>`;
 | 
						|
		} else if (this.messages?.length) {
 | 
						|
			return html`<tf-news
 | 
						|
				id="news"
 | 
						|
				whoami=${this.whoami}
 | 
						|
				.messages=${this.messages}
 | 
						|
				.users=${this.users}
 | 
						|
				.expanded=${this.expanded}
 | 
						|
				.drafts=${this.drafts}
 | 
						|
				@tf-expand=${this.on_expand}
 | 
						|
				@tf-draft=${this.draft}
 | 
						|
			></tf-news>`;
 | 
						|
		} else if (this.results?.length) {
 | 
						|
			let keys = Object.keys(this.results[0]).sort();
 | 
						|
			return html`<table style="width: 100%; max-width: 100%">
 | 
						|
				<tr>
 | 
						|
					${keys.map((key) => html`<th>${key}</th>`)}
 | 
						|
				</tr>
 | 
						|
				${this.results.map(
 | 
						|
					(row) =>
 | 
						|
						html`<tr>
 | 
						|
							${keys.map((key) => html`<td>${row[key]}</td>`)}
 | 
						|
						</tr>`
 | 
						|
				)}
 | 
						|
			</table>`;
 | 
						|
		} else {
 | 
						|
			return html`<div>No results.</div>`;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	render() {
 | 
						|
		if (this.query !== this.last_query) {
 | 
						|
			this.last_query = this.query;
 | 
						|
			this.search(this.query);
 | 
						|
		}
 | 
						|
		let self = this;
 | 
						|
		return html`
 | 
						|
			<style>${generate_theme()}</style>
 | 
						|
			<div class="w3-padding">
 | 
						|
				<div style="display: flex; flex-direction: row; gap: 4px">
 | 
						|
					<input type="text" class="w3-input w3-theme-d1" id="search" value=${this.query} style="flex: 1" @keydown=${this.search_keydown}></input>
 | 
						|
					<button class="w3-button w3-theme-d1" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}>Search</button>
 | 
						|
				</div>
 | 
						|
				${this.render_results()}
 | 
						|
			</div>
 | 
						|
		`;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
customElements.define('tf-tab-search', TfTabSearchElement);
 |