damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 0f86a41c4a5cae6eb431c2fd4c1343e1e4b10982
parent 957ac1dc03bf6a250fad08f24e323076049a9994
Author: Daniel D’Aquino <daniel@daquino.me>
Date:   Sat, 30 Sep 2023 03:16:58 +0000

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>

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 16++++++++++++----
Mdamus/Views/Events/BuilderEventView.swift | 69+++++++++++++++------------------------------------------------------
Adamus/Views/Events/EventLoaderView.swift | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adamus/Views/Events/EventMutingContainerView.swift | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Views/Events/EventShell.swift | 9++-------
Adamus/Views/Events/MentionView.swift | 32++++++++++++++++++++++++++++++++
Ddamus/Views/Events/MutedEventView.swift | 83-------------------------------------------------------------------------------
Mdamus/Views/Events/SelectedEventView.swift | 14++++++++++----
Mdamus/Views/Reposts/RepostedEvent.swift | 12+++++++++++-
Mdamus/Views/ThreadView.swift | 29++++++++++++++++++-----------
10 files changed, 281 insertions(+), 164 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -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 */, diff --git a/damus/Views/Events/BuilderEventView.swift b/damus/Views/Events/BuilderEventView.swift @@ -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() - } } } diff --git a/damus/Views/Events/EventLoaderView.swift b/damus/Views/Events/EventLoaderView.swift @@ -0,0 +1,87 @@ +// +// EventLoaderView.swift +// damus +// +// Created by Daniel D’Aquino 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) + } + } +} diff --git a/damus/Views/Events/EventMutingContainerView.swift b/damus/Views/Events/EventMutingContainerView.swift @@ -0,0 +1,94 @@ +// +// EventMutingContainerView.swift +// damus +// +// Created by William Casarin on 2023-01-27. +// + +import SwiftUI + +/// 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? + + @State var shown: Bool + + init(damus_state: DamusState, event: NostrEvent, @ViewBuilder content: () -> Content) { + self.damus_state = damus_state + self.event = event + 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 body: some View { + Group { + if should_mute { + if let customMuteBox { + customMuteBox($shown) + } + else { + EventMutedBoxView(shown: $shown) + } + } + if shown { + self.content + } + } + .onReceive(handle_notify(.new_mutes)) { mutes in + if mutes.contains(event.pubkey) { + shown = false + } + } + .onReceive(handle_notify(.new_unmutes)) { unmutes in + if unmutes.contains(event.pubkey) { + shown = true + } + } + } +} + +/// 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 { + + EventMutingContainerView(damus_state: test_damus_state, event: test_note) { + EventView(damus: test_damus_state, event: test_note) + } + .frame(width: .infinity, height: 50) + } +} diff --git a/damus/Views/Events/EventShell.swift b/damus/Views/Events/EventShell.swift @@ -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) } } diff --git a/damus/Views/Events/MentionView.swift b/damus/Views/Events/MentionView.swift @@ -0,0 +1,32 @@ +// +// MentionView.swift +// damus +// +// Created by Daniel D’Aquino 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)) + } +} diff --git a/damus/Views/Events/MutedEventView.swift b/damus/Views/Events/MutedEventView.swift @@ -1,83 +0,0 @@ -// -// MutedEventView.swift -// damus -// -// Created by William Casarin on 2023-01-27. -// - -import SwiftUI - -struct MutedEventView: View { - let damus_state: DamusState - let event: NostrEvent - - let selected: Bool - @State var shown: Bool - - init(damus_state: DamusState, event: NostrEvent, selected: Bool) { - self.damus_state = damus_state - self.event = event - self.selected = selected - self._shown = State(initialValue: should_show_event(keypair: damus_state.keypair, hellthreads: damus_state.muted_threads, contacts: damus_state.contacts, ev: event)) - } - - 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 shown { - Event - } - } - .onReceive(handle_notify(.new_mutes)) { mutes in - if mutes.contains(event.pubkey) { - shown = false - } - } - .onReceive(handle_notify(.new_unmutes)) { unmutes in - if unmutes.contains(event.pubkey) { - shown = true - } - } - } -} - -struct MutedEventView_Previews: PreviewProvider { - - static var previews: some View { - - MutedEventView(damus_state: test_damus_state, event: test_note, selected: false) - .frame(width: .infinity, height: 50) - } -} diff --git a/damus/Views/Events/SelectedEventView.swift b/damus/Views/Events/SelectedEventView.swift @@ -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 { diff --git a/damus/Views/Reposts/RepostedEvent.swift b/damus/Views/Reposts/RepostedEvent.swift @@ -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)) + } } } } diff --git a/damus/Views/ThreadView.swift b/damus/Views/ThreadView.swift @@ -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)