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`