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);
 |