1
0
mirror of git://jb55.com/damus synced 2024-09-30 00:40:45 +00:00

ui: Hide quoted or reposted notes from people whom the user has muted. (#1216)

Summary
-------

This patch fixes the issue where the user might see notes from users that they have muted, if such note has been reposted or quoted.

Furthermore, this patch introduces some improvements on some of the associated views, making them more reusable.

Testing of the fix
------------------

**PASS**

**Device:** iPhone 14 Pro simulator
**iOS:** 17.0
**Damus:** This commit
**Test steps:**

1. Create two test accounts (if not created already). We will use test account "A". Test account "B" is an external test account
2. Make some notes from test account "B" (if non existent)
3. Switch to account "A"
4. Under test account "A", follow account "B"
5. Repost a note from account "B", and quote another note from account "B"
6. Access "account B"'s timeline. Repost and quoted note should all be visible. Layout should look as usual
7. Click on the reposted note. Should appear and it should look normal
8. Click on the note with the quote. Should appear and it should look normal
9. Click on the quoted note. Should appear and it should look normal
10. Now mute account "B"
12. Go back to account "A"'s timeline
13. Repost should appear, but the reposted content should be hidden behind a mute box. Clicking on show/hide should show or hide muted content
14. Note with quoted content should appear, but the quoted content should be hidden behind a mute box. Clicking on show/hide should work as expected
15. Make sure that the layout in steps 13 and 14 look good.
16. Click on the repost to access the thread view. Should be muted as expected.
17. Add a comment to the repost. Comment should appear even if the mute box hides the main note
18. Click on the note with quote to open its thread view. Comments should appear, main note should appear, but quoted content should be behind the mute box
19. Under account "B", add a comment to the quoted notes
20. Under account "A", check in the thread view that "B"'s reply is behind a mute box
21. Reply to the note with the quote. Check that the note appears correctly and that quoted content is behind the mute box (in the post composer view)
22. Find on Nostr a post where one of the replies contains a quoted note. Mute the user of the quoted content, and check that quoted content is now in a mute box

Smoke sanity test
-----------------

**PASS**

**Device:** iPhone 14 pro simulator
**iOS:** 16.4
**Test steps:** Browse a timeline filled with real notes and comments. Go through different notes and threads, mute some users, just to make sure nothing else appears obsviously broken.

Other notes
-----------

I removed this code:

```
.frame(maxWidth: .infinity, minHeight: PFP_SIZE)
```

from `EventShell`, because it was causing the layout to break on "threaded" style event view with muted quoted content (e.g. in a reply with quoted content).

The line of code dates back to `495859e07f`, but I am not sure why this line existed in the first place, or if removing it has any negative impact.

Closes: https://github.com/damus-io/damus/issues/1216
Changelog-Fixed: Hide quoted or reposted notes from people whom the user has muted. (#1216)
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
Daniel D’Aquino 2023-09-30 03:16:58 +00:00 committed by William Casarin
parent 957ac1dc03
commit 0f86a41c4a
9 changed files with 232 additions and 115 deletions

View File

@ -364,7 +364,7 @@
4CF0ABE12981A83900D66079 /* MutelistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE02981A83900D66079 /* MutelistView.swift */; };
4CF0ABE32981BC7D00D66079 /* UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE22981BC7D00D66079 /* UserView.swift */; };
4CF0ABE52981EE0C00D66079 /* EULAView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE42981EE0C00D66079 /* EULAView.swift */; };
4CF0ABE7298444FD00D66079 /* MutedEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE6298444FC00D66079 /* MutedEventView.swift */; };
4CF0ABE7298444FD00D66079 /* EventMutingContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE6298444FC00D66079 /* EventMutingContainerView.swift */; };
4CF0ABE929844AF100D66079 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE829844AF100D66079 /* AnyCodable.swift */; };
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */; };
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
@ -427,6 +427,8 @@
D71DC1EC2A9129C3006E207C /* PostViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71DC1EB2A9129C3006E207C /* PostViewTests.swift */; };
D723C38E2AB8D83400065664 /* ContentFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723C38D2AB8D83400065664 /* ContentFilters.swift */; };
D78525252A7B2EA4002FA637 /* NoteContentViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78525242A7B2EA4002FA637 /* NoteContentViewTests.swift */; };
D7870BC12AC4750B0080BA88 /* MentionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7870BC02AC4750B0080BA88 /* MentionView.swift */; };
D7870BC32AC47EBC0080BA88 /* EventLoaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7870BC22AC47EBC0080BA88 /* EventLoaderView.swift */; };
D7DEEF2F2A8C021E00E0C99F /* NostrEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */; };
E4FA1C032A24BB7F00482697 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */; };
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
@ -1048,7 +1050,7 @@
4CF0ABE02981A83900D66079 /* MutelistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutelistView.swift; sourceTree = "<group>"; };
4CF0ABE22981BC7D00D66079 /* UserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserView.swift; sourceTree = "<group>"; };
4CF0ABE42981EE0C00D66079 /* EULAView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EULAView.swift; sourceTree = "<group>"; };
4CF0ABE6298444FC00D66079 /* MutedEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutedEventView.swift; sourceTree = "<group>"; };
4CF0ABE6298444FC00D66079 /* EventMutingContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMutingContainerView.swift; sourceTree = "<group>"; };
4CF0ABE829844AF100D66079 /* AnyCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodable.swift; sourceTree = "<group>"; };
4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = "<group>"; };
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
@ -1110,6 +1112,8 @@
D71DC1EB2A9129C3006E207C /* PostViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewTests.swift; sourceTree = "<group>"; };
D723C38D2AB8D83400065664 /* ContentFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentFilters.swift; sourceTree = "<group>"; };
D78525242A7B2EA4002FA637 /* NoteContentViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContentViewTests.swift; sourceTree = "<group>"; };
D7870BC02AC4750B0080BA88 /* MentionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionView.swift; sourceTree = "<group>"; };
D7870BC22AC47EBC0080BA88 /* EventLoaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventLoaderView.swift; sourceTree = "<group>"; };
D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrEventTests.swift; sourceTree = "<group>"; };
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = "<group>"; };
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
@ -2001,12 +2005,14 @@
4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */,
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */,
4CC7AAF9297F64AC00430951 /* EventMenu.swift */,
4CF0ABE6298444FC00D66079 /* MutedEventView.swift */,
4CF0ABE6298444FC00D66079 /* EventMutingContainerView.swift */,
4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */,
4C3D52B7298DB5C6001C5831 /* TextEvent.swift */,
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */,
4CA9275B2A28FF570098A105 /* Longform */,
4CA927602A290E340098A105 /* EventShell.swift */,
D7870BC02AC4750B0080BA88 /* MentionView.swift */,
D7870BC22AC47EBC0080BA88 /* EventLoaderView.swift */,
);
path = Events;
sourceTree = "<group>";
@ -2558,6 +2564,7 @@
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */,
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */,
D7870BC12AC4750B0080BA88 /* MentionView.swift in Sources */,
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */,
4C363AA228296A7E006E126D /* SearchView.swift in Sources */,
4CC7AAED297F0B9E00430951 /* Highlight.swift in Sources */,
@ -2639,7 +2646,7 @@
4CD348EF29C3659D00497EB2 /* ImageUploadModel.swift in Sources */,
4C7D096E2A0AEA0400943473 /* ScannerCoordinator.swift in Sources */,
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */,
4CF0ABE7298444FD00D66079 /* MutedEventView.swift in Sources */,
4CF0ABE7298444FD00D66079 /* EventMutingContainerView.swift in Sources */,
9C83F89329A937B900136C08 /* TextViewWrapper.swift in Sources */,
4C1253502A76C5B20004F4B8 /* UnfollowedNotify.swift in Sources */,
4C86F7C62A76C51100EC0817 /* AttachedWalletNotify.swift in Sources */,
@ -2689,6 +2696,7 @@
4C1A9A2329DDDB8100516EAC /* IconLabel.swift in Sources */,
4CA352AC2A76C07F003BB08B /* NewUnmutesNotify.swift in Sources */,
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
D7870BC32AC47EBC0080BA88 /* EventLoaderView.swift in Sources */,
4CE879522996B68900F758CC /* RelayType.swift in Sources */,
4CE8795B2996C47A00F758CC /* ZapsModel.swift in Sources */,
4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */,

View File

@ -10,71 +10,38 @@ import SwiftUI
struct BuilderEventView: View {
let damus: DamusState
let event_id: NoteId
@State var event: NostrEvent?
@State var subscription_uuid: String = UUID().description
let event: NostrEvent?
init(damus: DamusState, event: NostrEvent) {
_event = State(initialValue: event)
self.event = event
self.damus = damus
self.event_id = event.id
}
init(damus: DamusState, event_id: NoteId) {
let event = damus.events.lookup(event_id)
self.event_id = event_id
self.damus = damus
_event = State(initialValue: event)
self.event = nil
}
func unsubscribe() {
damus.pool.unsubscribe(sub_id: subscription_uuid)
}
func subscribe(filters: [NostrFilter]) {
damus.pool.register_handler(sub_id: subscription_uuid, handler: handle_event)
damus.pool.send(.subscribe(.init(filters: filters, sub_id: subscription_uuid)))
}
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
guard case .nostr_event(let nostr_response) = ev else {
return
}
guard case .event(let id, let nostr_event) = nostr_response else {
return
}
guard id == subscription_uuid else {
return
}
if event != nil {
return
}
event = nostr_event
unsubscribe()
}
func load() {
subscribe(filters: [
NostrFilter(ids: [self.event_id], limit: 1)
])
func Event(event: NostrEvent) -> some View {
return EventView(damus: damus, event: event, options: .embedded)
.padding([.top, .bottom], 8)
.onTapGesture {
let ev = event.get_inner_event(cache: damus.events) ?? event
let thread = ThreadModel(event: ev, damus_state: damus)
damus.nav.push(route: .Thread(thread: thread))
}
}
var body: some View {
VStack {
if let event {
EventView(damus: damus, event: event, options: .embedded)
.padding([.top, .bottom], 8)
.onTapGesture {
let ev = event.get_inner_event(cache: damus.events) ?? event
let thread = ThreadModel(event: ev, damus_state: damus)
damus.nav.push(route: .Thread(thread: thread))
}
self.Event(event: event)
} else {
ProgressView().padding()
EventLoaderView(damus_state: damus, event_id: self.event_id) { loaded_event in
self.Event(event: loaded_event)
}
}
}
.frame(minWidth: 0, maxWidth: .infinity)
@ -82,12 +49,6 @@ struct BuilderEventView: View {
RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray.opacity(0.2), lineWidth: 1.0)
)
.onAppear {
guard event == nil else {
return
}
self.load()
}
}
}

View File

@ -0,0 +1,87 @@
//
// EventLoaderView.swift
// damus
//
// Created by Daniel DAquino on 2023-09-27.
//
import SwiftUI
/// This view handles the loading logic for Nostr events, so that you can easily use views that require `NostrEvent`, even if you only have a `NoteId`
struct EventLoaderView<Content: View>: View {
let damus_state: DamusState
let event_id: NoteId
@State var event: NostrEvent?
@State var subscription_uuid: String = UUID().description
let content: (NostrEvent) -> Content
init(damus_state: DamusState, event_id: NoteId, @ViewBuilder content: @escaping (NostrEvent) -> Content) {
self.damus_state = damus_state
self.event_id = event_id
self.content = content
let event = damus_state.events.lookup(event_id)
_event = State(initialValue: event)
}
func unsubscribe() {
damus_state.pool.unsubscribe(sub_id: subscription_uuid)
}
func subscribe(filters: [NostrFilter]) {
damus_state.pool.register_handler(sub_id: subscription_uuid, handler: handle_event)
damus_state.pool.send(.subscribe(.init(filters: filters, sub_id: subscription_uuid)))
}
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
guard case .nostr_event(let nostr_response) = ev else {
return
}
guard case .event(let id, let nostr_event) = nostr_response else {
return
}
guard id == subscription_uuid else {
return
}
if event != nil {
return
}
event = nostr_event
unsubscribe()
}
func load() {
subscribe(filters: [
NostrFilter(ids: [self.event_id], limit: 1)
])
}
var body: some View {
VStack {
if let event {
self.content(event)
} else {
ProgressView().padding()
}
}
.onAppear {
guard event == nil else {
return
}
self.load()
}
}
}
struct EventLoaderView_Previews: PreviewProvider {
static var previews: some View {
EventLoaderView(damus_state: test_damus_state, event_id: test_note.id) { event in
EventView(damus: test_damus_state, event: event)
}
}
}

View File

@ -1,5 +1,5 @@
//
// MutedEventView.swift
// EventMutingContainerView.swift
// damus
//
// Created by William Casarin on 2023-01-27.
@ -7,57 +7,45 @@
import SwiftUI
struct MutedEventView: View {
/// A container view that shows or hides provided content based on whether the given event should be muted or not, with built-in user controls to show or hide content, and an option to customize the muted box
struct EventMutingContainerView<Content: View>: View {
typealias MuteBoxViewClosure = ((_ shown: Binding<Bool>) -> AnyView)
let damus_state: DamusState
let event: NostrEvent
let content: Content
var customMuteBox: MuteBoxViewClosure?
let selected: Bool
@State var shown: Bool
init(damus_state: DamusState, event: NostrEvent, selected: Bool) {
init(damus_state: DamusState, event: NostrEvent, @ViewBuilder content: () -> Content) {
self.damus_state = damus_state
self.event = event
self.selected = selected
self.content = content()
self._shown = State(initialValue: should_show_event(keypair: damus_state.keypair, hellthreads: damus_state.muted_threads, contacts: damus_state.contacts, ev: event))
}
init(damus_state: DamusState, event: NostrEvent, muteBox: @escaping MuteBoxViewClosure, @ViewBuilder content: () -> Content) {
self.init(damus_state: damus_state, event: event, content: content)
self.customMuteBox = muteBox
}
var should_mute: Bool {
return !should_show_event(keypair: damus_state.keypair, hellthreads: damus_state.muted_threads, contacts: damus_state.contacts, ev: event)
}
var MutedBox: some View {
ZStack {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(DamusColors.adaptableGrey)
HStack {
Text("Note from a user you've muted", comment: "Text to indicate that what is being shown is a note from a user who has been muted.")
Spacer()
Button(shown ? NSLocalizedString("Hide", comment: "Button to hide a note from a user who has been muted.") : NSLocalizedString("Show", comment: "Button to show a note from a user who has been muted.")) {
shown.toggle()
}
}
.padding(10)
}
}
var Event: some View {
Group {
if selected {
SelectedEventView(damus: damus_state, event: event, size: .selected)
} else {
EventView(damus: damus_state, event: event)
}
}
}
var body: some View {
Group {
if should_mute {
MutedBox
if let customMuteBox {
customMuteBox($shown)
}
else {
EventMutedBoxView(shown: $shown)
}
}
if shown {
Event
self.content
}
}
.onReceive(handle_notify(.new_mutes)) { mutes in
@ -73,11 +61,34 @@ struct MutedEventView: View {
}
}
/// A box that instructs the user about a content that has been muted.
struct EventMutedBoxView: View {
@Binding var shown: Bool
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(DamusColors.adaptableGrey)
HStack {
Text("Note from a user you've muted", comment: "Text to indicate that what is being shown is a note from a user who has been muted.")
Spacer()
Button(shown ? NSLocalizedString("Hide", comment: "Button to hide a note from a user who has been muted.") : NSLocalizedString("Show", comment: "Button to show a note from a user who has been muted.")) {
shown.toggle()
}
}
.padding(10)
}
}
}
struct MutedEventView_Previews: PreviewProvider {
static var previews: some View {
MutedEventView(damus_state: test_damus_state, event: test_note, selected: false)
EventMutingContainerView(damus_state: test_damus_state, event: test_note) {
EventView(damus: test_damus_state, event: test_note)
}
.frame(width: .infinity, height: 50)
}
}

View File

@ -42,10 +42,6 @@ struct EventShell<Content: View>: View {
return first_eref_mention(ev: event, keypair: state.keypair)
}
func Mention(_ mention: Mention<NoteId>) -> some View {
return BuilderEventView(damus: state, event_id: mention.ref)
}
var ActionBar: some View {
return EventActionBar(damus_state: state, event: event)
@ -78,7 +74,7 @@ struct EventShell<Content: View>: View {
content
if let mention = get_mention() {
Mention(mention)
MentionView(damus_state: state, mention: mention)
}
if has_action_bar {
@ -107,7 +103,7 @@ struct EventShell<Content: View>: View {
content
if !options.contains(.no_mentions), let mention = get_mention() {
Mention(mention)
MentionView(damus_state: state, mention: mention)
.padding(.horizontal)
}
@ -128,7 +124,6 @@ struct EventShell<Content: View>: View {
}
.contentShape(Rectangle())
.id(event.id)
.frame(maxWidth: .infinity, minHeight: PFP_SIZE)
.padding([.bottom], 2)
}
}

View File

@ -0,0 +1,32 @@
//
// MentionView.swift
// damus
//
// Created by Daniel DAquino on 2023-09-27.
//
import SwiftUI
struct MentionView: View {
let damus_state: DamusState
let mention: Mention<NoteId>
init(damus_state: DamusState, mention: Mention<NoteId>) {
self.damus_state = damus_state
self.mention = mention
}
var body: some View {
EventLoaderView(damus_state: damus_state, event_id: mention.ref) { event in
EventMutingContainerView(damus_state: damus_state, event: event) {
BuilderEventView(damus: damus_state, event_id: mention.ref)
}
}
}
}
struct MentionView_Previews: PreviewProvider {
static var previews: some View {
MentionView(damus_state: test_damus_state, mention: .note(test_note.id))
}
}

View File

@ -55,10 +55,7 @@ struct SelectedEventView: View {
EventBody(damus_state: damus, event: event, size: size, options: [.wide])
if let mention = first_eref_mention(ev: event, keypair: damus.keypair) {
BuilderEventView(damus: damus, event_id: mention.ref)
.padding(.horizontal)
}
Mention
Text(verbatim: "\(format_date(event.created_at))")
.padding([.top, .leading, .trailing])
@ -88,6 +85,15 @@ struct SelectedEventView: View {
.compositingGroup()
}
}
var Mention: some View {
Group {
if let mention = first_eref_mention(ev: event, keypair: damus.keypair) {
MentionView(damus_state: damus, mention: mention)
.padding(.horizontal)
}
}
}
}
struct SelectedEventView_Previews: PreviewProvider {

View File

@ -22,7 +22,17 @@ struct RepostedEvent: View {
.buttonStyle(PlainButtonStyle())
//SelectedEventView(damus: damus, event: inner_ev, size: .normal)
EventView(damus: damus, event: inner_ev, pubkey: inner_ev.pubkey, options: options.union(.wide))
EventMutingContainerView(
damus_state: damus,
event: inner_ev,
muteBox: { event_shown in
AnyView(
EventMutedBoxView(shown: event_shown)
.padding(.horizontal, 5) // Add a bit of horizontal padding to avoid the mute box from touching the edges of the screen
)
}) {
EventView(damus: damus, event: inner_ev, pubkey: inner_ev.pubkey, options: options.union(.wide))
}
}
}
}

View File

@ -41,10 +41,9 @@ struct ThreadView: View {
LazyVStack {
// MARK: - Parents events view
ForEach(parent_events, id: \.id) { parent_event in
MutedEventView(damus_state: state,
event: parent_event,
selected: false)
EventMutingContainerView(damus_state: state, event: parent_event) {
EventView(damus: state, event: parent_event)
}
.padding(.horizontal)
.onTapGesture {
thread.set_active_event(parent_event, keypair: self.state.keypair)
@ -68,11 +67,18 @@ struct ThreadView: View {
})
// MARK: - Actual event view
MutedEventView(
EventMutingContainerView(
damus_state: state,
event: self.thread.event,
selected: true
)
muteBox: { event_shown in
AnyView(
EventMutedBoxView(shown: event_shown)
.padding(5)
)
}
) {
SelectedEventView(damus: state, event: self.thread.event, size: .selected)
}
.id(self.thread.event.id)
/*
@ -83,11 +89,12 @@ struct ThreadView: View {
*/
ForEach(sorted_child_events, id: \.id) { child_event in
MutedEventView(
EventMutingContainerView(
damus_state: state,
event: child_event,
selected: false
)
event: child_event
) {
EventView(damus: state, event: child_event)
}
.padding(.horizontal)
.onTapGesture {
thread.set_active_event(child_event, keypair: state.keypair)