From 4d3e42812d944ed2ce28c9e5edf1e8c7ad1e68e4 Mon Sep 17 00:00:00 2001 From: Cory McWilliams <cory@unprompted.com> Date: Sat, 31 May 2025 15:17:07 -0400 Subject: [PATCH] ssb: Condense follows/blocks more, and support replies to them. #122 --- apps/ssb.json | 2 +- apps/ssb/tf-message.js | 189 ++++++++++++++++++++++++++++++++--------- apps/ssb/tf-news.js | 10 ++- 3 files changed, 159 insertions(+), 42 deletions(-) diff --git a/apps/ssb.json b/apps/ssb.json index 2b3b67b6..952be215 100644 --- a/apps/ssb.json +++ b/apps/ssb.json @@ -1,5 +1,5 @@ { "type": "tildefriends-app", "emoji": "🦀", - "previous": "&n2E4F4hnQe0dz+NvcMlKl5pcAZ3a1NM7/iNyWng9fRQ=.sha256" + "previous": "&Ky/Q/lCC3DIcqbsO9KAnfKzeBE/e9CB/8C5jACZ3UDI=.sha256" } diff --git a/apps/ssb/tf-message.js b/apps/ssb/tf-message.js index 45af0003..b54d82ff 100644 --- a/apps/ssb/tf-message.js +++ b/apps/ssb/tf-message.js @@ -301,31 +301,35 @@ class TfMessageElement extends LitElement { return total; } + expanded_key() { + return this.message?.id || this.messages?.map((x) => x.id).join(':'); + } + set_expanded(expanded, tag) { + let key = this.expanded_key(); this.dispatchEvent( new CustomEvent('tf-expand', { bubbles: true, composed: true, - detail: {id: (this.message.id || '') + (tag || ''), expanded: expanded}, + detail: {id: key + (tag || ''), expanded: expanded}, }) ); } toggle_expanded(tag) { - this.set_expanded( - !this.expanded[(this.message.id || '') + (tag || '')], - tag - ); + let key = this.expanded_key(); + this.set_expanded(!this.expanded[key + (tag || '')], tag); } is_expanded(tag) { - return this.expanded[(this.message.id || '') + (tag || '')]; + let key = this.expanded_key(); + return this.expanded[key + (tag || '')]; } render_children() { let self = this; if (this.message.child_messages?.length) { - if (!this.expanded[this.message.id]) { + if (!this.expanded[this.expanded_key()]) { return html` <button class="w3-button w3-theme-d1 w3-block w3-bar" @@ -578,6 +582,44 @@ class TfMessageElement extends LitElement { `; } + content_group_by_author() { + let sorted = this.message.messages + .map((x) => [ + x.author, + x.content.blocking !== undefined + ? x.content.blocking + ? 'is blocking' + : 'is no longer blocking' + : x.content.following !== undefined + ? x.content.following + ? 'is following' + : 'is no longer following' + : '', + x.content.contact, + x, + ]) + .sort(); + let result = []; + let last; + let group; + for (let row of sorted) { + if (last && last[0] == row[0] && last[1] == row[1]) { + group.push(row[2]); + } else { + if (group) { + result.push({author: last[0], action: last[1], users: group}); + } + last = row; + group = [row[2]]; + } + } + if (group) { + result.push({author: last[0], action: last[1], users: group}); + } + console.log(this.message.messages, result); + return result; + } + render() { let content = this.message?.content; if (this.message?.decrypted?.type == 'post') { @@ -586,20 +628,54 @@ class TfMessageElement extends LitElement { let class_background = this.class_background(); let self = this; if (this.message?.type === 'contact_group') { - return this.render_frame( - html` ${this.message.messages.map( - (x) => - html`<tf-message - .message=${x} - whoami=${this.whoami} - .users=${this.users} - .drafts=${this.drafts} - .expanded=${this.expanded} - channel=${this.channel} - channel_unread=${this.channel_unread} - ></tf-message>` - )}` - ); + if (this.expanded[this.expanded_key()]) { + return this.render_frame(html` + <div class="w3-padding"> + ${this.message.messages.map( + (x) => + html`<tf-message + .message=${x} + whoami=${this.whoami} + .users=${this.users} + .drafts=${this.drafts} + .expanded=${this.expanded} + channel=${this.channel} + channel_unread=${this.channel_unread} + ></tf-message>` + )} + </div> + <button + class="w3-button w3-theme-d1 w3-block w3-bar" + style="box-sizing: border-box" + @click=${() => self.set_expanded(false)} + > + Collapse + </button> + `); + } else { + return this.render_frame(html` + <div class="w3-padding"> + ${this.content_group_by_author().map( + (x) => html` + <tf-user id=${x.author} .users=${this.users}></tf-user> + ${x.action} + ${x.users.map( + (y) => html` + <tf-user id=${y} .users=${this.users}></tf-user> + ` + )} + ` + )} + </div> + <button + class="w3-button w3-theme-d1 w3-block w3-bar" + style="box-sizing: border-box" + @click=${() => self.set_expanded(true)} + > + Expand + </button> + `); + } } else if (this.message.placeholder) { return this.render_frame( html`<div class="w3-padding"> @@ -679,25 +755,60 @@ class TfMessageElement extends LitElement { </div> `); } else if (content.type == 'contact') { - return html` - <div class="w3-padding"> - <tf-user id=${this.message.author} .users=${this.users}></tf-user> - is - ${content.blocking === true - ? 'blocking' - : content.blocking === false - ? 'no longer blocking' - : content.following === true - ? 'following' - : content.following === false - ? 'no longer following' - : '?'} - <tf-user - id=${this.message.content.contact} - .users=${this.users} - ></tf-user> + return this.render_frame(html` + <div class="w3-bar"> + <div class="w3-bar-item"> + <tf-user id=${this.message.author} .users=${this.users}></tf-user> + is + ${content.blocking === true + ? 'blocking' + : content.blocking === false + ? 'no longer blocking' + : content.following === true + ? 'following' + : content.following === false + ? 'no longer following' + : '?'} + <tf-user + id=${this.message.content.contact} + .users=${this.users} + ></tf-user> + </div> + <div class="w3-bar-item w3-right"> + <button class="w3-button w3-theme-d1" @click=${this.toggle_menu}> + % + </button> + <div + class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1" + style="right: 48px" + > + <a + target="_top" + class="w3-button w3-bar-item" + href=${'#' + encodeURIComponent(this.message?.id)} + >View Message</a + > + <button + class="w3-button w3-bar-item w3-border-bottom" + @click=${this.copy_id} + > + Copy ID + </button> + ${this.drafts[this.message?.id] === undefined + ? html` + <button + class="w3-button w3-bar-item" + @click=${this.show_reply} + > + ↩️ Reply + </button> + ` + : undefined} + </div> + </div> + ${this.render_votes()} ${this.render_actions()} </div> - `; + `); } else if (content.type == 'post') { let self = this; let body; diff --git a/apps/ssb/tf-news.js b/apps/ssb/tf-news.js index 3e21a1da..aea87e6f 100644 --- a/apps/ssb/tf-news.js +++ b/apps/ssb/tf-news.js @@ -166,7 +166,10 @@ class TfNewsElement extends LitElement { if (message?.content?.type === 'contact') { group.push(message); } else { - if (group.length > 0) { + if (group.length == 1) { + result.push(group[0]); + group = []; + } else if (group.length > 1) { result.push({ rowid: Math.max(...group.map((x) => x.rowid)), type: 'contact_group', @@ -177,7 +180,10 @@ class TfNewsElement extends LitElement { result.push(message); } } - if (group.length > 0) { + if (group.length == 1) { + result.push(group[0]); + group = []; + } else if (group.length > 1) { result.push({ rowid: Math.max(...group.map((x) => x.rowid)), type: 'contact_group',