diff --git a/apps/ssb.json b/apps/ssb.json index 81d1f20c..0c7f7294 100644 --- a/apps/ssb.json +++ b/apps/ssb.json @@ -1,5 +1,5 @@ { "type": "tildefriends-app", "emoji": "🐌", - "previous": "&pySy2RopLJGGrJRpoyzKM7zDzQUCxRXLuE62kS4C5s4=.sha256" + "previous": "&0gBRfD+3EaZD2S82zAnXT3hgGuNTnUncOh5vGwZwbSw=.sha256" } diff --git a/apps/ssb/tf-app.js b/apps/ssb/tf-app.js index 6e7e3701..b18ab4e5 100644 --- a/apps/ssb/tf-app.js +++ b/apps/ssb/tf-app.js @@ -267,6 +267,34 @@ class TfElement extends LitElement { } } + async get_latest_private(following) { + let latest = (await tfrpc.rpc.query('SELECT MAX(rowid) AS latest FROM messages'))[0].latest; + const k_chunk_count = 256; + while (latest - k_chunk_count >= 0) { + let messages = await tfrpc.rpc.query(` + SELECT messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature + FROM messages + JOIN json_each(?1) AS following ON messages.author = following.value + WHERE + messages.rowid > ?2 AND + messages.rowid <= ?3 AND + json(messages.content) LIKE '"%' + ORDER BY sequence DESC + `, + [ + JSON.stringify(following), + latest - k_chunk_count, + latest, + ]); + messages = (await this.decrypt(messages)).filter(x => x.decrypted); + if (messages.length) { + return Math.max(...messages.map(x => x.rowid)); + } + latest -= k_chunk_count; + }; + return -1; + } + async load() { let whoami = this.whoami; let following = await tfrpc.rpc.following([whoami], 2); @@ -302,6 +330,7 @@ class TfElement extends LitElement { '"' + this.whoami.replace('"', '""') + '"', ] ); + let latest_private = this.get_latest_private(Object.keys(following)); this.channels_unread = JSON.parse( (await tfrpc.rpc.databaseGet('unread')) ?? '{}' ); @@ -320,6 +349,11 @@ class TfElement extends LitElement { this.channels_latest = Object.fromEntries( channels.map((x) => [x.channel, x.rowid]) ); + 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); + }); this.following = Object.keys(following); this.users = users; console.log(`load finished ${whoami} => ${this.whoami}`); @@ -333,6 +367,30 @@ class TfElement extends LitElement { tfrpc.rpc.databaseSet('unread', JSON.stringify(this.channels_unread)); } + async decrypt(messages) { + let whoami = this.whoami; + return Promise.all(messages.map(async function (message) { + let content; + try { + content = JSON.parse(message?.content); + } catch {} + if (typeof content === 'string') { + let decrypted; + try { + decrypted = await tfrpc.rpc.try_decrypt(whoami, content); + } catch {} + if (decrypted) { + try { + message.decrypted = JSON.parse(decrypted); + } catch { + message.decrypted = decrypted; + } + } + } + return message; + })); + } + render_tab() { let following = this.following; let users = this.users; diff --git a/apps/ssb/tf-tab-news-feed.js b/apps/ssb/tf-tab-news-feed.js index aa061218..609a8960 100644 --- a/apps/ssb/tf-tab-news-feed.js +++ b/apps/ssb/tf-tab-news-feed.js @@ -151,6 +151,21 @@ class TfTabNewsFeedElement extends LitElement { ); } result = [].concat(...(await Promise.all(promises))); + } else if (this.hash == '#πŸ”') { + result = await tfrpc.rpc.query( + ` + SELECT messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature + FROM messages + JOIN json_each(?1) AS following ON messages.author = following.value + WHERE + messages.timestamp >= ?2 AND + messages.timestamp < ?3 AND + json(messages.content) LIKE '"%' + ORDER BY sequence DESC + `, + [JSON.stringify(this.following), start_time, end_time] + ); + result = (await this.decrypt(result)).filter(x => x.decrypted); } else { let promises = []; const k_following_limit = 256; diff --git a/apps/ssb/tf-tab-news.js b/apps/ssb/tf-tab-news.js index c7f7ce78..9202d5de 100644 --- a/apps/ssb/tf-tab-news.js +++ b/apps/ssb/tf-tab-news.js @@ -210,6 +210,12 @@ class TfTabNewsElement extends LitElement { style=${this.hash == '#@' ? 'font-weight: bold' : undefined} >@mentions ${this.unread_status('@')} + πŸ”private ${this.unread_status('πŸ”')} ${this.channels.map( (x) => html`