ssb: Merge the query tab into the search tab. Search for something starting with sql: to search for arbitrary SQL.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🦀",
|
"emoji": "🦀",
|
||||||
"previous": "&wggp4XYiydgBSvQfWqGXXPtkgZ3XWkGLzkiBmghCyd8=.sha256"
|
"previous": "&J8epgiTHHI/2GtNoS+FW3UpyTrIEL4ltovW1JpmwSW4=.sha256"
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,6 @@ import * as tf_tab_news from './tf-tab-news.js';
|
|||||||
import * as tf_tab_news_feed from './tf-tab-news-feed.js';
|
import * as tf_tab_news_feed from './tf-tab-news-feed.js';
|
||||||
import * as tf_tab_search from './tf-tab-search.js';
|
import * as tf_tab_search from './tf-tab-search.js';
|
||||||
import * as tf_tab_connections from './tf-tab-connections.js';
|
import * as tf_tab_connections from './tf-tab-connections.js';
|
||||||
import * as tf_tab_query from './tf-tab-query.js';
|
|
||||||
import * as tf_tag from './tf-tag.js';
|
import * as tf_tag from './tf-tag.js';
|
||||||
import * as tf_styles from './tf-styles.js';
|
import * as tf_styles from './tf-styles.js';
|
||||||
|
|
||||||
|
@@ -206,8 +206,6 @@ class TfElement extends LitElement {
|
|||||||
this.tab = 'search';
|
this.tab = 'search';
|
||||||
} else if (this.hash === '#connections') {
|
} else if (this.hash === '#connections') {
|
||||||
this.tab = 'connections';
|
this.tab = 'connections';
|
||||||
} else if (this.hash.startsWith('#sql=')) {
|
|
||||||
this.tab = 'query';
|
|
||||||
} else {
|
} else {
|
||||||
this.tab = 'news';
|
this.tab = 'news';
|
||||||
}
|
}
|
||||||
@@ -736,17 +734,6 @@ class TfElement extends LitElement {
|
|||||||
: null}
|
: null}
|
||||||
></tf-tab-search>
|
></tf-tab-search>
|
||||||
`;
|
`;
|
||||||
} else if (this.tab === 'query') {
|
|
||||||
return html`
|
|
||||||
<tf-tab-query
|
|
||||||
.following=${this.following}
|
|
||||||
whoami=${this.whoami}
|
|
||||||
.users=${this.users}
|
|
||||||
query=${this.hash?.startsWith('#sql=')
|
|
||||||
? this.hash.substring(5)
|
|
||||||
: null}
|
|
||||||
></tf-tab-query>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,8 +744,6 @@ class TfElement extends LitElement {
|
|||||||
await tfrpc.rpc.setHash('#');
|
await tfrpc.rpc.setHash('#');
|
||||||
} else if (tab === 'connections') {
|
} else if (tab === 'connections') {
|
||||||
await tfrpc.rpc.setHash('#connections');
|
await tfrpc.rpc.setHash('#connections');
|
||||||
} else if (tab === 'query') {
|
|
||||||
await tfrpc.rpc.setHash('#sql=');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,7 +777,6 @@ class TfElement extends LitElement {
|
|||||||
'📰': 'news',
|
'📰': 'news',
|
||||||
'📡': 'connections',
|
'📡': 'connections',
|
||||||
'🔍': 'search',
|
'🔍': 'search',
|
||||||
'👩💻': 'query',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tabs = html`
|
let tabs = html`
|
||||||
|
@@ -1,136 +0,0 @@
|
|||||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
|
||||||
import {styles} from './tf-styles.js';
|
|
||||||
|
|
||||||
class TfTabQueryElement extends LitElement {
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
whoami: {type: String},
|
|
||||||
users: {type: Object},
|
|
||||||
following: {type: Array},
|
|
||||||
query: {type: String},
|
|
||||||
expanded: {type: Object},
|
|
||||||
results: {type: Array},
|
|
||||||
error: {type: Object},
|
|
||||||
duration: {type: Number},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = styles;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
let self = this;
|
|
||||||
this.whoami = null;
|
|
||||||
this.users = {};
|
|
||||||
this.following = [];
|
|
||||||
this.expanded = {};
|
|
||||||
this.duration = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
async search(query) {
|
|
||||||
console.log('Searching...', this.whoami, query);
|
|
||||||
this.results = [];
|
|
||||||
this.error = undefined;
|
|
||||||
this.duration = undefined;
|
|
||||||
let search = this.renderRoot.getElementById('search');
|
|
||||||
if (search) {
|
|
||||||
search.value = query;
|
|
||||||
search.focus();
|
|
||||||
}
|
|
||||||
await tfrpc.rpc.setHash('#sql=' + encodeURIComponent(query));
|
|
||||||
let start_time = new Date();
|
|
||||||
try {
|
|
||||||
this.results = await tfrpc.rpc.query(query, []);
|
|
||||||
} catch (error) {
|
|
||||||
this.error = error;
|
|
||||||
}
|
|
||||||
let end_time = new Date();
|
|
||||||
this.duration = (end_time - start_time).valueOf();
|
|
||||||
console.log('Done.');
|
|
||||||
search = this.renderRoot.getElementById('search');
|
|
||||||
if (search) {
|
|
||||||
search.value = query;
|
|
||||||
search.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
search_keydown(event) {
|
|
||||||
if (event.keyCode == 13 && event.ctrlKey) {
|
|
||||||
this.query = this.renderRoot.getElementById('search').value;
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render_results() {
|
|
||||||
if (!this.results?.length) {
|
|
||||||
return html`<div>No results.</div>`;
|
|
||||||
} else {
|
|
||||||
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>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render_error() {
|
|
||||||
if (this.error) {
|
|
||||||
return html`<h2 style="color: red">${this.error.message}</h2>
|
|
||||||
<pre style="color: red">${this.error.stack}</pre>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.query !== this.last_query) {
|
|
||||||
this.last_query = this.query;
|
|
||||||
this.search(this.query);
|
|
||||||
}
|
|
||||||
let self = this;
|
|
||||||
return html`
|
|
||||||
<div style="display: flex; flex-direction: row; gap: 4px">
|
|
||||||
<textarea
|
|
||||||
id="search"
|
|
||||||
rows="8"
|
|
||||||
class="w3-input w3-theme-d1"
|
|
||||||
style="flex: 1; resize: vertical"
|
|
||||||
@keydown=${this.search_keydown}
|
|
||||||
>
|
|
||||||
${this.query}</textarea
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="w3-button w3-theme-d1"
|
|
||||||
@click=${(event) =>
|
|
||||||
self.search(self.renderRoot.getElementById('search').value)}
|
|
||||||
>
|
|
||||||
Execute
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div ?hidden=${this.duration === undefined}>
|
|
||||||
Took ${this.duration / 1000.0} seconds.
|
|
||||||
</div>
|
|
||||||
<div ?hidden=${this.duration !== undefined}>Executing...</div>
|
|
||||||
${this.render_error()} ${this.render_results()}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('tf-tab-query', TfTabQueryElement);
|
|
@@ -11,6 +11,9 @@ class TfTabSearchElement extends LitElement {
|
|||||||
following: {type: Array},
|
following: {type: Array},
|
||||||
query: {type: String},
|
query: {type: String},
|
||||||
expanded: {type: Object},
|
expanded: {type: Object},
|
||||||
|
messages: {type: Array},
|
||||||
|
results: {type: Array},
|
||||||
|
error: {type: Object},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +41,21 @@ class TfTabSearchElement extends LitElement {
|
|||||||
search.select();
|
search.select();
|
||||||
}
|
}
|
||||||
await tfrpc.rpc.setHash('#q=' + encodeURIComponent(query));
|
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(
|
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
|
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
@@ -55,7 +73,8 @@ class TfTabSearchElement extends LitElement {
|
|||||||
search.focus();
|
search.focus();
|
||||||
search.select();
|
search.select();
|
||||||
}
|
}
|
||||||
this.renderRoot.getElementById('news').messages = results;
|
this.messages = results;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
search_keydown(event) {
|
search_keydown(event) {
|
||||||
@@ -87,6 +106,39 @@ class TfTabSearchElement extends LitElement {
|
|||||||
tfrpc.rpc.localStorageSet('drafts', JSON.stringify(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() {
|
render() {
|
||||||
if (this.query !== this.last_query) {
|
if (this.query !== this.last_query) {
|
||||||
this.last_query = this.query;
|
this.last_query = this.query;
|
||||||
@@ -94,11 +146,13 @@ class TfTabSearchElement extends LitElement {
|
|||||||
}
|
}
|
||||||
let self = this;
|
let self = this;
|
||||||
return html`
|
return html`
|
||||||
|
<div class="w3-padding">
|
||||||
<div style="display: flex; flex-direction: row; gap: 4px">
|
<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>
|
<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>
|
<button class="w3-button w3-theme-d1" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}>Search</button>
|
||||||
</div>
|
</div>
|
||||||
<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>
|
${this.render_results()}
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,6 @@ options:
|
|||||||
ssb_port (default: 8008): Port on which to listen for SSB secure handshake connections.
|
ssb_port (default: 8008): Port on which to listen for SSB secure handshake connections.
|
||||||
http_local_only (default: false): Whether to bind http(s) to the loopback address. Otherwise any.
|
http_local_only (default: false): Whether to bind http(s) to the loopback address. Otherwise any.
|
||||||
http_port (default: 12345): Port on which to listen for HTTP connections.
|
http_port (default: 12345): Port on which to listen for HTTP connections.
|
||||||
https_port (default: 0): Port on which to listen for secure HTTP connections.
|
|
||||||
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
||||||
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
||||||
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
||||||
|
Reference in New Issue
Block a user