diff --git a/apps/ssb.json b/apps/ssb.json index 68e42478..99196bfd 100644 --- a/apps/ssb.json +++ b/apps/ssb.json @@ -1,5 +1,5 @@ { "type": "tildefriends-app", "emoji": "🦀", - "previous": "&UOHm9X6pa1L0G0QdtiIqV/UW4vH/4rTA4i30IPqMzeQ=.sha256" + "previous": "&5YZ9ja1NymBZsDaHIrpgZCzn95R34Y1RQug6gy4EULU=.sha256" } diff --git a/apps/ssb/tf-app.js b/apps/ssb/tf-app.js index 55f13ee9..10f90ec6 100644 --- a/apps/ssb/tf-app.js +++ b/apps/ssb/tf-app.js @@ -38,8 +38,8 @@ class TfElement extends LitElement { this.channels = []; this.channels_unread = {}; this.channels_latest = {}; - this.loading_channels_latest = 0; - this.loading_channels_latest_scheduled = 0; + this.loading_latest = 0; + this.loading_latest_scheduled = 0; tfrpc.rpc.getBroadcasts().then((b) => { self.broadcasts = b || []; }); @@ -147,7 +147,8 @@ class TfElement extends LitElement { } } - async fetch_about(ids, users) { + async fetch_about(following, users) { + let ids = Object.keys(following).sort(); const k_cache_version = 1; let cache = await tfrpc.rpc.databaseGet('about'); let original_cache = cache; @@ -225,7 +226,11 @@ class TfElement extends LitElement { } users = users || {}; for (let id of Object.keys(cache.about)) { - users[id] = Object.assign(users[id] || {}, cache.about[id]); + users[id] = Object.assign( + {follow_depth: following[id]?.d}, + users[id] || {}, + cache.about[id] + ); } return Object.assign({}, users); } @@ -248,7 +253,7 @@ class TfElement extends LitElement { this.load_channels(); } } - this.schedule_load_channels_latest(); + this.schedule_load_latest(); } async _handle_whoami_changed(event) { @@ -307,7 +312,6 @@ class TfElement extends LitElement { ranges.push([i, Math.min(i + k_chunk_size, latest), true]); } } - console.log(cache); for (let range of ranges) { let messages = await tfrpc.rpc.query( ` @@ -340,112 +344,111 @@ class TfElement extends LitElement { JSON.stringify(cache) ); } - console.log(cache); return cache.latest; } async load_channels_latest(following) { - this.loading_channels_latest++; - try { - let start_time = new Date(); - let latest_private = this.get_latest_private(following); - let channels = await tfrpc.rpc.query( - ` - SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages - JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value - JOIN json_each(?2) AS following ON messages.author = following.value - WHERE - messages.content ->> 'type' = 'post' AND - messages.content ->> 'root' IS NULL AND - messages.author != ?4 - GROUP by channel - UNION - SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages - JOIN json_each(?2) AS following ON messages.author = following.value - WHERE - messages.content ->> 'type' = 'post' AND - messages.content ->> 'root' IS NULL AND - messages.author != ?4 - UNION - SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3) - JOIN messages ON messages.rowid = messages_fts.rowid - JOIN json_each(?2) AS following ON messages.author = following.value - WHERE messages.author != ?4 - `, - [ - JSON.stringify(this.channels), - JSON.stringify(following), - '"' + this.whoami.replace('"', '""') + '"', - this.whoami, - ] - ); - this.channels_latest = Object.fromEntries( - channels.map((x) => [x.channel, x.rowid]) - ); - console.log('latest', this.channels_latest); - console.log('unread', this.channels_unread); - console.log('channels took', (new Date() - start_time) / 1000.0); - let self = this; - latest_private.then(function (latest) { - self.channels_latest = Object.assign({}, self.channels_latest, { - '🔐': latest, - }); - console.log('private took', (new Date() - start_time) / 1000.0); + let start_time = new Date(); + let latest_private = this.get_latest_private(following); + let channels = await tfrpc.rpc.query( + ` + SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages + JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value + JOIN json_each(?2) AS following ON messages.author = following.value + WHERE + messages.content ->> 'type' = 'post' AND + messages.content ->> 'root' IS NULL AND + messages.author != ?4 + GROUP by channel + UNION + SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages + JOIN json_each(?2) AS following ON messages.author = following.value + WHERE + messages.content ->> 'type' = 'post' AND + messages.content ->> 'root' IS NULL AND + messages.author != ?4 + UNION + SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3) + JOIN messages ON messages.rowid = messages_fts.rowid + JOIN json_each(?2) AS following ON messages.author = following.value + WHERE messages.author != ?4 + `, + [ + JSON.stringify(this.channels), + JSON.stringify(following), + '"' + this.whoami.replace('"', '""') + '"', + this.whoami, + ] + ); + this.channels_latest = Object.fromEntries( + channels.map((x) => [x.channel, x.rowid]) + ); + console.log('latest', this.channels_latest); + console.log('unread', this.channels_unread); + console.log('channels took', (new Date() - start_time) / 1000.0); + let self = this; + latest_private.then(function (latest) { + self.channels_latest = Object.assign({}, self.channels_latest, { + '🔐': latest, }); - } finally { - this.loading_channels_latest--; - } + console.log('private took', (new Date() - start_time) / 1000.0); + }); } - _schedule_load_channels_latest_timer() { - --this.loading_channels_latest_scheduled; - this.schedule_load_channels_latest(); + _schedule_load_latest_timer() { + --this.loading_latest_scheduled; + this.schedule_load_latest(); } - schedule_load_channels_latest() { - if (!this.loading_channels_latest) { + schedule_load_latest() { + if (!this.loading_latest) { this.shadowRoot.getElementById('tf-tab-news')?.load_latest(); - this.load_channels_latest(this.following); - } else if (!this.loading_channels_latest_scheduled) { - this.loading_channels_latest_scheduled++; - setTimeout(this._schedule_load_channels_latest_timer.bind(this), 5000); + this.load(); + } else if (!this.loading_latest_scheduled) { + this.loading_latest_scheduled++; + setTimeout(this._schedule_load_latest_timer.bind(this), 5000); } } async load() { - let start_time = new Date(); - let whoami = this.whoami; - let following = await tfrpc.rpc.following([whoami], 2); - let users = {}; - let by_count = []; - for (let [id, v] of Object.entries(following)) { - users[id] = { - following: v.of, - blocking: v.ob, - followed: v.if, - blocked: v.ib, - }; - by_count.push({count: v.of, id: id}); + this.loading_latest = true; + try { + let start_time = new Date(); + let whoami = this.whoami; + let following = await tfrpc.rpc.following([whoami], 2); + let users = {}; + let by_count = []; + for (let [id, v] of Object.entries(following)) { + users[id] = { + following: v.of, + blocking: v.ob, + followed: v.if, + blocked: v.ib, + }; + by_count.push({count: v.of, id: id}); + } + this.load_channels_latest(Object.keys(following)); + this.channels_unread = JSON.parse( + (await tfrpc.rpc.databaseGet('unread')) ?? '{}' + ); + this.following = Object.keys(following); + users = await this.fetch_about(following, users); + console.log( + 'about took', + (new Date() - start_time) / 1000.0, + 'seconds for', + Object.keys(users).length, + 'users' + ); + this.users = users; + console.log( + `load finished ${whoami} => ${this.whoami} in ${(new Date() - start_time) / 1000}` + ); + this.whoami = whoami; + this.loaded = whoami; + } finally { + this.loading_latest = false; } - this.load_channels_latest(Object.keys(following)); - this.channels_unread = JSON.parse( - (await tfrpc.rpc.databaseGet('unread')) ?? '{}' - ); - users = await this.fetch_about(Object.keys(following).sort(), users); - console.log( - 'about took', - (new Date() - start_time) / 1000.0, - 'seconds for', - Object.keys(users).length, - 'users' - ); - this.following = Object.keys(following); - this.users = users; - console.log( - `load finished ${whoami} => ${this.whoami} in ${(new Date() - start_time) / 1000}` - ); - this.whoami = whoami; - this.loaded = whoami; } channel_set_unread(event) { diff --git a/apps/ssb/tf-message.js b/apps/ssb/tf-message.js index d8d48fe7..90377e71 100644 --- a/apps/ssb/tf-message.js +++ b/apps/ssb/tf-message.js @@ -589,9 +589,7 @@ class TfMessageElement extends LitElement { let image; let description; if (content.name !== undefined) { - name = html`
- Name: ${content.name} -
`; + name = html`
Name: ${content.name}
`; } if (content.image !== undefined) { image = html` @@ -600,18 +598,14 @@ class TfMessageElement extends LitElement { } if (content.description !== undefined) { description = html` -
+
${unsafeHTML(tfutils.markdown(content.description))}
`; } let update = content.about == this.message.author - ? html`
- Updated profile. -
` + ? html`
Updated profile.
` : html`
Updated profile for . @@ -759,10 +753,10 @@ class TfMessageElement extends LitElement { return this.render_small_frame(html`

- ${content.subscribed ? 'subscribed to' : 'unsubscribed from'} - #${content.channel} + ${content.subscribed ? 'subscribed to' : 'unsubscribed from'} + #${content.channel}

`); diff --git a/apps/ssb/tf-tab-connections.js b/apps/ssb/tf-tab-connections.js index ecb5d0ea..8769ae67 100644 --- a/apps/ssb/tf-tab-connections.js +++ b/apps/ssb/tf-tab-connections.js @@ -142,14 +142,15 @@ class TfTabConnectionsElement extends LitElement { }, {}) ); return html` - ${connection.connected ? html` - - ` + ${connection.connected + ? html` + + ` : undefined} ${connection.flags.one_shot ? '🔃' : undefined} @@ -270,16 +271,19 @@ class TfTabConnectionsElement extends LitElement {
${this.identities.map( (x) => - html`
+ html`
${x == this.server_identity - ? html`
🖥 local server
` + ? html`
+ 🖥 local server +
` : undefined} ${this.my_identities.indexOf(x) != -1 - ? html`
😎 you
` + ? html`
+ 😎 you +
` : undefined}
` diff --git a/apps/ssb/tf-user.js b/apps/ssb/tf-user.js index 9051509a..4ba4c079 100644 --- a/apps/ssb/tf-user.js +++ b/apps/ssb/tf-user.js @@ -19,8 +19,10 @@ class TfUserElement extends LitElement { } render() { + let user = this.users[this.id]; + let shape = user && user.follow_depth >= 2 ? 'w3-circle' : 'w3-round'; let image = html`?`; @@ -29,13 +31,13 @@ class TfUserElement extends LitElement { >${name !== undefined ? name : this.id}`; - if (this.users[this.id]) { - let image_link = this.users[this.id].image; + if (user) { + let image_link = user.image; image_link = typeof image_link == 'string' ? image_link : image_link?.link; if (image_link !== undefined) { image = html``; diff --git a/src/ssb.c b/src/ssb.c index b4243518..3a160e52 100644 --- a/src/ssb.c +++ b/src/ssb.c @@ -1356,8 +1356,7 @@ bool tf_ssb_connection_is_client(tf_ssb_connection_t* connection) bool tf_ssb_connection_is_connected(tf_ssb_connection_t* connection) { - return (connection->state == k_tf_ssb_state_verified || connection->state == k_tf_ssb_state_server_verified) && - !connection->is_closing; + return (connection->state == k_tf_ssb_state_verified || connection->state == k_tf_ssb_state_server_verified) && !connection->is_closing; } bool tf_ssb_connection_is_closing(tf_ssb_connection_t* connection) diff --git a/src/ssb.db.c b/src/ssb.db.c index 63d4dde3..70d5a894 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -1455,6 +1455,7 @@ tf_ssb_following_t* tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, in result[write_index].blocking_count = following[i]->blocking_count; result[write_index].followed_by_count = following[i]->ref_count; result[write_index].blocked_by_count = following[i]->block_ref_count; + result[write_index].depth = following[i]->depth; write_index++; } } diff --git a/src/ssb.db.h b/src/ssb.db.h index 6315ec56..737af03a 100644 --- a/src/ssb.db.h +++ b/src/ssb.db.h @@ -286,6 +286,8 @@ typedef struct _tf_ssb_following_t int followed_by_count; /** The number of known users blocking the account. */ int blocked_by_count; + /** Degree of separation between initial accounts and this account. */ + int depth; /** The account's identity. */ char id[k_id_base64_len]; } tf_ssb_following_t; diff --git a/src/ssb.js.c b/src/ssb.js.c index 160c32de..f158769f 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -2187,6 +2187,7 @@ static void _tf_ssb_following_after_work(tf_ssb_t* ssb, int status, void* user_d JS_SetPropertyStr(context, entry, "ob", JS_NewInt32(context, following->out_following[i].blocking_count)); JS_SetPropertyStr(context, entry, "if", JS_NewInt32(context, following->out_following[i].followed_by_count)); JS_SetPropertyStr(context, entry, "ib", JS_NewInt32(context, following->out_following[i].blocked_by_count)); + JS_SetPropertyStr(context, entry, "d", JS_NewInt32(context, following->out_following[i].depth)); JS_SetPropertyStr(context, object, following->out_following[i].id, entry); } JSValue result = JS_Call(context, following->promise[0], JS_UNDEFINED, 1, &object); diff --git a/src/ssb.rpc.c b/src/ssb.rpc.c index 13784265..5f5d8875 100644 --- a/src/ssb.rpc.c +++ b/src/ssb.rpc.c @@ -446,8 +446,7 @@ static void _tf_ssb_rpc_send_endpoints(tf_ssb_t* ssb) for (int i = 0; i < count; i++) { char id[k_id_base64_len] = { 0 }; - if ((tf_ssb_connection_is_attendant(connections[i]) || tf_ssb_connection_is_endpoint(connections[i])) && - tf_ssb_connection_is_connected(connections[i]) && + if ((tf_ssb_connection_is_attendant(connections[i]) || tf_ssb_connection_is_endpoint(connections[i])) && tf_ssb_connection_is_connected(connections[i]) && tf_ssb_connection_get_id(connections[i], id, sizeof(id))) { JS_SetPropertyUint32(context, endpoints, id_count++, JS_NewString(context, id));