Compare commits

..

14 Commits

12 changed files with 140 additions and 71 deletions

View File

@ -4,7 +4,8 @@
<script> <script>
const g_data = $data; const g_data = $data;
</script> </script>
<link rel="stylesheet" href="w3.css"></link> <link rel="stylesheet" href="w3.css" />
<!-- prettier-ignore -->
<style> <style>
/* 2018 Valiant Poppy */ /* 2018 Valiant Poppy */
.w3-theme-l5 {color:#000 !important; background-color:#fbf3f3 !important} .w3-theme-l5 {color:#000 !important; background-color:#fbf3f3 !important}

View File

@ -42,10 +42,27 @@ window.addEventListener('load', function () {
} else if (description.type === 'textarea') { } else if (description.type === 'textarea') {
return html` return html`
<li class="w3-row"> <li class="w3-row">
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold">${key}</label> <label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold"
>${key}</label
>
<div class="w3-rest w3-padding">${description.description}</div> <div class="w3-rest w3-padding">${description.description}</div>
<textarea class="w3-input" style="vertical-align: top; resize: vertical" id=${'gs_' + key}>${description.value}</textarea> <textarea
<button class="w3-button w3-right w3-quarter w3-theme-action" @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.value)}>Set</button> class="w3-input"
style="vertical-align: top; resize: vertical"
id=${'gs_' + key}
>
${description.value}</textarea
>
<button
class="w3-button w3-right w3-quarter w3-theme-action"
@click=${(e) =>
global_settings_set(
key,
e.srcElement.previousElementSibling.value
)}
>
Set
</button>
</li> </li>
`; `;
} else { } else {
@ -61,13 +78,17 @@ window.addEventListener('load', function () {
} }
const user_template = (user, permissions) => html` const user_template = (user, permissions) => html`
<li class="w3-card w3-margin"> <li class="w3-card w3-margin">
<button class="w3-button w3-theme-action" @click=${(e) => delete_user(user)}>Delete</button> <button
class="w3-button w3-theme-action"
@click=${(e) => delete_user(user)}
>
Delete
</button>
${user}: ${permissions.map((x) => permission_template(x))} ${user}: ${permissions.map((x) => permission_template(x))}
</li> </li>
`; `;
const users_template = (users) => const users_template = (users) =>
html` html` <header class="w3-container w3-theme-l2"><h2>Users</h2></header>
<header class="w3-container w3-theme-l2"><h2>Users</h2></header>
<ul class="w3-ul"> <ul class="w3-ul">
${Object.entries(users).map((u) => user_template(u[0], u[1]))} ${Object.entries(users).map((u) => user_template(u[0], u[1]))}
</ul>`; </ul>`;

View File

@ -116,16 +116,18 @@ async function main() {
<div class="w3-card-4 w3-margin"> <div class="w3-card-4 w3-margin">
<header class="w3-container w3-theme-l2"><h2>Identities</h2></header> <header class="w3-container w3-theme-l2"><h2>Identities</h2></header>
<ul class="w3-ul">` + <ul class="w3-ul">` +
ids ids
.map( .map(
(id) => `<li style="overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis"> (
id
) => `<li style="overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis">
<button onclick="handler.export_id(event)" data-id="${id}" class="w3-button w3-theme">Export Identity</button> <button onclick="handler.export_id(event)" data-id="${id}" class="w3-button w3-theme">Export Identity</button>
<button onclick="handler.delete_id(event)" data-id="${id}" class="w3-button w3-theme">Delete Identity</button> <button onclick="handler.delete_id(event)" data-id="${id}" class="w3-button w3-theme">Delete Identity</button>
${id} ${id}
</li>` </li>`
) )
.join('\n') + .join('\n') +
` </ul> ` </ul>
</div> </div>
</body>` </body>`
); );

View File

@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🐌", "emoji": "🐌",
"previous": "&raSj7ozmSDNGmB6TtjDk7oOiTc33ZN+RrBMASJ2F4cA=.sha256" "previous": "&wA6sLaDxtYeFdVCCu8jyhPsGYtGZEjbWQHeGOn0Yifg=.sha256"
} }

View File

@ -345,13 +345,15 @@ class TfElement extends LitElement {
([k, v]) => html` ([k, v]) => html`
<button <button
title=${v} title=${v}
class="w3-bar-item w3-padding-large w3-hover-theme tab ${self.tab == class="w3-bar-item w3-padding w3-hover-theme tab ${self.tab == v
v
? 'w3-theme-l2' ? 'w3-theme-l2'
: 'w3-theme-l1'}" : 'w3-theme-l1'}"
@click=${() => self.set_tab(v)} @click=${() => self.set_tab(v)}
> >
${k} ${k}
<span class=${self.tab == v ? '' : 'w3-hide-small'}
>${v.charAt(0).toUpperCase() + v.substring(1)}</span
>
</button> </button>
` `
)} )}
@ -359,10 +361,12 @@ class TfElement extends LitElement {
`; `;
let contents = !this.loaded let contents = !this.loaded
? this.loading ? this.loading
? html`<div class="w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge"> ? html`<div
Loading... class="w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge"
</div> >
${this.render_tab()}` Loading...
</div>
${this.render_tab()}`
: html`<div>Select or create an identity.</div>` : html`<div>Select or create an identity.</div>`
: this.render_tab(); : this.render_tab();
return html` return html`

View File

@ -295,14 +295,18 @@ class TfComposeElement extends LitElement {
{ {
values: values, values: values,
selectTemplate: function (item) { selectTemplate: function (item) {
return item ? `[@${item.original.key}](${item.original.value})` : undefined; return item
? `[@${item.original.key}](${item.original.value})`
: undefined;
}, },
}, },
{ {
trigger: '&', trigger: '&',
values: this.autocomplete, values: this.autocomplete,
selectTemplate: function (item) { selectTemplate: function (item) {
return item ? `![${item.original.key}](${item.original.value})` : undefined; return item
? `![${item.original.key}](${item.original.value})`
: undefined;
}, },
}, },
], ],
@ -544,7 +548,7 @@ class TfComposeElement extends LitElement {
@paste=${this.paste} @paste=${this.paste}
contenteditable contenteditable
.innerText=${live(draft.text ?? '')} .innerText=${live(draft.text ?? '')}
></span> ></span>
</div> </div>
<div class="w3-half w3-padding"> <div class="w3-half w3-padding">
${content_warning} ${content_warning}

View File

@ -247,9 +247,7 @@ ${JSON.stringify(mention, null, 2)}</pre
if (mentions.length) { if (mentions.length) {
let self = this; let self = this;
return html` return html`
<fieldset <fieldset style="padding: 0.5em; border: 1px solid black">
style="padding: 0.5em; border: 1px solid black"
>
<legend>Mentions</legend> <legend>Mentions</legend>
${mentions.map((x) => self.render_mention(x))} ${mentions.map((x) => self.render_mention(x))}
</fieldset> </fieldset>

View File

@ -105,6 +105,16 @@ class TfTabConnectionsElement extends LitElement {
} }
render_connection(connection) { render_connection(connection) {
let requests = Object.values(
connection.requests.reduce(function (accumulator, value) {
let key = `${value.name}:${Math.sign(value.request_number)}`;
if (!accumulator[key]) {
accumulator[key] = Object.assign({count: 0}, value);
}
accumulator[key].count++;
return accumulator;
}, {})
);
return html` return html`
<button <button
class="w3-button w3-theme-d1" class="w3-button w3-theme-d1"
@ -116,9 +126,20 @@ class TfTabConnectionsElement extends LitElement {
${connection.tunnel !== undefined ${connection.tunnel !== undefined
? '🚇' ? '🚇'
: html`(${connection.host}:${connection.port})`} : html`(${connection.host}:${connection.port})`}
<div>${connection.requests.map(x => html` <div>
<span class="w3-tag w3-small">${x.request_number > 0 ? '🟩' : '🟥'} ${x.name}</span> ${requests.map(
`)}</div> (x) => html`
<span class="w3-tag w3-small"
>${x.request_number > 0 ? '🟩' : '🟥'} ${x.name}
<span
class="w3-badge w3-white"
style=${x.count > 1 ? undefined : 'display: none'}
>${x.count}</span
></span
>
`
)}
</div>
<ul> <ul>
${this.connections ${this.connections
.filter((x) => x.tunnel === this.connections.indexOf(connection)) .filter((x) => x.tunnel === this.connections.indexOf(connection))
@ -187,12 +208,16 @@ class TfTabConnectionsElement extends LitElement {
${this.identities.map( ${this.identities.map(
(x) => (x) =>
html`<li class="w3-bar"> html`<li class="w3-bar">
${x == this.server_identity ? ${x == this.server_identity
html`<span class="w3-tag w3-medium w3-round w3-theme-l1">🖥 local server</span>` : ? html`<span class="w3-tag w3-medium w3-round w3-theme-l1"
undefined} >🖥 local server</span
${this.my_identities.indexOf(x) != -1 ? >`
html`<span class="w3-tag w3-medium w3-round w3-theme-d1">😎 you</span>` : : undefined}
undefined} ${this.my_identities.indexOf(x) != -1
? html`<span class="w3-tag w3-medium w3-round w3-theme-d1"
>😎 you</span
>`
: undefined}
<tf-user id=${x} .users=${this.users}></tf-user> <tf-user id=${x} .users=${this.users}></tf-user>
</li>` </li>`
)} )}

View File

@ -115,13 +115,17 @@ class TfTabNewsElement extends LitElement {
></tf-profile>` ></tf-profile>`
: undefined; : undefined;
let edit_profile; let edit_profile;
if (!this.loading && if (
!this.loading &&
this.users[this.whoami]?.name === undefined && this.users[this.whoami]?.name === undefined &&
this.hash.substring(1) != this.whoami) { this.hash.substring(1) != this.whoami
edit_profile = html` ) {
<div class="w3-panel w3-padding w3-round w3-card-4 w3-theme-l3"> edit_profile = html` <div
Follow your identity link above to edit your profile and set your name. class="w3-panel w3-padding w3-round w3-card-4 w3-theme-l3"
</div>`; >
Follow your identity link above to edit your profile and set your
name.
</div>`;
} }
return html` return html`
<p class="w3-bar"> <p class="w3-bar">

View File

@ -19,6 +19,10 @@ class TfUserElement extends LitElement {
} }
render() { render() {
let image = html`<span
class="w3-theme-light w3-circle"
style="display: inline-block; width: 2em; height: 2em; text-align: center; line-height: 2em"
>?</span>`;
let name = this.users?.[this.id]?.name; let name = this.users?.[this.id]?.name;
name = name =
name !== undefined name !== undefined
@ -26,21 +30,20 @@ class TfUserElement extends LitElement {
: html`<a target="_top" href=${'#' + this.id}>${this.id}</a>`; : html`<a target="_top" href=${'#' + this.id}>${this.id}</a>`;
if (this.users[this.id]) { if (this.users[this.id]) {
let image = this.users[this.id].image; let image_link = this.users[this.id].image;
image = typeof image == 'string' ? image : image?.link; image_link = typeof image_link == 'string' ? image_link : image_link?.link;
return html` <div style="display: inline-block; font-weight: bold"> if (image_link !== undefined) {
<img image = html`<img
style="width: 2em; height: 2em; vertical-align: middle; border-radius: 50%" class="w3-circle"
?hidden=${image === undefined} style="width: 2em; height: 2em; vertical-align: middle"
src="${image ? '/' + image + '/view' : undefined}" src="/${image_link}/view"
/> />`;
${name} }
</div>`;
} else {
return html` <div style="display: inline-block; font-weight: bold">
${name}
</div>`;
} }
return html` <div style="display: inline-block; font-weight: bold">
${image}
${name}
</div>`;
} }
} }

View File

@ -737,12 +737,11 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
} }
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous, bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
size_t out_previous_size, char* out_author, size_t out_author_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags)
size_t out_signature_size, int* out_flags)
{ {
bool found = false; bool found = false;
sqlite3_stmt* statement; sqlite3_stmt* statement;
const char* query = "SELECT id, previous, author, timestamp, json(content), hash, signature, flags FROM messages WHERE author = ?1 AND sequence = ?2"; const char* query = "SELECT id, previous, timestamp, json(content), hash, signature, flags FROM messages WHERE author = ?1 AND sequence = ?2";
sqlite3* db = tf_ssb_acquire_db_reader(ssb); sqlite3* db = tf_ssb_acquire_db_reader(ssb);
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK) if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
{ {
@ -766,29 +765,25 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
snprintf(out_previous, out_previous_size, "%s", (const char*)sqlite3_column_text(statement, 1)); snprintf(out_previous, out_previous_size, "%s", (const char*)sqlite3_column_text(statement, 1));
} }
} }
if (out_author)
{
snprintf(out_author, out_author_size, "%s", (const char*)sqlite3_column_text(statement, 2));
}
if (out_timestamp) if (out_timestamp)
{ {
*out_timestamp = sqlite3_column_double(statement, 3); *out_timestamp = sqlite3_column_double(statement, 2);
} }
if (out_content) if (out_content)
{ {
*out_content = tf_strdup((const char*)sqlite3_column_text(statement, 4)); *out_content = tf_strdup((const char*)sqlite3_column_text(statement, 3));
} }
if (out_hash) if (out_hash)
{ {
snprintf(out_hash, out_hash_size, "%s", (const char*)sqlite3_column_text(statement, 5)); snprintf(out_hash, out_hash_size, "%s", (const char*)sqlite3_column_text(statement, 4));
} }
if (out_signature) if (out_signature)
{ {
snprintf(out_signature, out_signature_size, "%s", (const char*)sqlite3_column_text(statement, 6)); snprintf(out_signature, out_signature_size, "%s", (const char*)sqlite3_column_text(statement, 5));
} }
if (out_flags) if (out_flags)
{ {
*out_flags = sqlite3_column_int(statement, 7); *out_flags = sqlite3_column_int(statement, 6);
} }
found = true; found = true;
} }
@ -1834,8 +1829,8 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id)
char hash[32]; char hash[32];
char signature[256]; char signature[256];
int flags = 0; int flags = 0;
if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, i, message_id, sizeof(message_id), previous, sizeof(previous), NULL, 0, &timestamp, &content, hash, if (tf_ssb_db_get_message_by_author_and_sequence(
sizeof(hash), signature, sizeof(signature), &flags)) ssb, id, i, message_id, sizeof(message_id), previous, sizeof(previous), &timestamp, &content, hash, sizeof(hash), signature, sizeof(signature), &flags))
{ {
JSValue message = tf_ssb_format_message(context, previous, id, i, timestamp, hash, content, signature, flags); JSValue message = tf_ssb_format_message(context, previous, id, i, timestamp, hash, content, signature, flags);
char calculated_id[k_id_base64_len]; char calculated_id[k_id_base64_len];

View File

@ -122,13 +122,19 @@ JSValue tf_ssb_db_get_message_by_id(tf_ssb_t* ssb, const char* id, bool is_keys)
** @param sequence The message sequence number. ** @param sequence The message sequence number.
** @param[out] out_message_id Populated with the message identifier. ** @param[out] out_message_id Populated with the message identifier.
** @param out_message_id_size The size of the out_message_id buffer. ** @param out_message_id_size The size of the out_message_id buffer.
** @param[out] out_previous Populated with the previous message identifier.
** @param out_previous_size The size of the out_previous buffer.
** @param[out] out_timestamp Populated with the timestamp. ** @param[out] out_timestamp Populated with the timestamp.
** @param[out] out_content Populated with the message content. Free with tf_free(). ** @param[out] out_content Populated with the message content. Free with tf_free().
** @param[out] out_hash Populated with the message hash format.
** @param out_hash_size The size of the out_hash buffer.
** @param[out] out_signature Populated with the message signature.
** @param out_signature_size The size of the out_signature buffer.
** @param[out] out_flags Populated with flags describing the format of the message.
** @return True if the message was found and retrieved. ** @return True if the message was found and retrieved.
*/ */
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous, bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
size_t out_previous_size, char* out_author, size_t out_author_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags);
size_t out_signature_size, int* out_flags);
/** /**
** Get information about the last message from an author. ** Get information about the last message from an author.
@ -380,6 +386,12 @@ bool tf_ssb_db_set_property(tf_ssb_t* ssb, const char* id, const char* key, cons
*/ */
void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callback)(const char* path, void* user_data), void* user_data); void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callback)(const char* path, void* user_data), void* user_data);
/**
** Verify an author's feed.
** @param ssb The SSB instance.
** @param id The author'd identity.
** @return true If the feed verified successfully.
*/
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id); bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id);
/** /**